diff --git a/lib/plan_page_detail.dart b/lib/plan_page_detail.dart index 7247597..73b7675 100644 --- a/lib/plan_page_detail.dart +++ b/lib/plan_page_detail.dart @@ -142,12 +142,17 @@ class _PlanPageDetailState extends State { toolbarHeight: 40, title: Text(_planTitle.toString()), ), - body: _planId == null - ? const Center( - child: Text( - 'Plan ID가 전달되지 않았습니다.', + body: _planId == null && _planTitle == null && _planDetails == null + ? Center( // 초기 로딩 상태 또는 인자 오류 + child: _planTitle == 'Error' + ? const Text( + '플랜 정보를 불러올 수 없습니다.', style: TextStyle(fontSize: 18, color: Colors.red), - ), + textAlign: TextAlign.center, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ) + : const CircularProgressIndicator(), ) : FutureBuilder>( future: _planDetails, @@ -155,82 +160,136 @@ class _PlanPageDetailState extends State { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } else if (snapshot.hasError) { - return Center(child: Text('Error: ${snapshot.error}')); + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text('Error loading details: ${snapshot.error}', textAlign: TextAlign.center, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ))); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { return const Center(child: Text('세부 계획 데이터가 없습니다.')); } else { final details = snapshot.data!; - return ListView.builder( - itemCount: details.length, - itemBuilder: (context, index) { - final item = details[index]; - return Card( - margin: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), - child: ListTile( - leading: item.thumbnail.isNotEmpty - ? SizedBox( - width: 100, - height: 100, - child: Image.network( - item.thumbnail, - fit: BoxFit.cover, - errorBuilder: (context, error, stackTrace) { - return const Icon(Icons.broken_image, size: 40, color: Colors.grey); + + // 첫 번째 비디오의 URL을 가져와 _selectedYoutubeUrl을 초기화합니다. + if (_selectedYoutubeUrl == null && details.isNotEmpty) { + _selectedYoutubeUrl = details.firstWhere( + (item) => item.lessonUrl.isNotEmpty && + (item.lessonUrl.contains('youtube.com') || + item.lessonUrl.contains('youtu.be')), + orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: ''), + ).lessonUrl.isNotEmpty ? details.firstWhere( + (item) => item.lessonUrl.isNotEmpty && + (item.lessonUrl.contains('youtube.com') || + item.lessonUrl.contains('youtu.be')), + orElse: () => PlanDetailItem(lessonId: '', lessonTag: '', lessonUrl: '', thumbnail: ''), + ).lessonUrl : null; + } + + return Column( // ListView와 YoutubePlayerPage를 세로로 배치하기 위해 Column 사용 + children: [ + SizedBox( + height: 150, // 가로 스크롤 리스트의 높이 + child: ListView.builder( + scrollDirection: Axis.horizontal, + padding: const EdgeInsets.symmetric(horizontal: 8.0), + itemCount: details.length, + itemBuilder: (context, index) { + final item = details[index]; + return GestureDetector( + onTap: () { + if (item.lessonUrl.isNotEmpty && + (item.lessonUrl.contains('youtube.com') || + item.lessonUrl.contains('youtu.be'))) { + setState(() { + _selectedYoutubeUrl = item.lessonUrl; + }); + } else { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + '유효한 YouTube URL이 아닙니다: ${item.lessonUrl}')), + ); + } }, - 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, - ), - ); - }, - ), - ) - : Container( - width: 100, - height: 100, - color: Colors.grey[200], - child: const Icon(Icons.image_not_supported, size: 40, color: Colors.grey), - ), - title: Text(item.lessonTag, style: const TextStyle(fontWeight: FontWeight.bold)), - subtitle: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text('ID: ${item.lessonId}', style: TextStyle(fontSize: 12, color: Colors.grey[700])), - const SizedBox(height: 2), - Text( - item.lessonUrl, - style: const TextStyle(fontSize: 12, color: Colors.blueAccent), - overflow: TextOverflow.ellipsis, - maxLines: 2, - ), - ], - ), - isThreeLine: true, - onTap: () { - if (item.lessonUrl.isNotEmpty && (item.lessonUrl.contains('youtube.com') || item.lessonUrl.contains('youtu.be'))) { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => YoutubePlayerPage( - lessonUrl: item.lessonUrl, - // currentBottomNavIndex: _currentBottomNavIndex, // 현재 탭 인덱스 전달 - ), + child: Container( + width: 110, + margin: const EdgeInsets.symmetric( + horizontal: 8.0, vertical: 8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 100, + height: 100, + child: ClipRRect( + borderRadius: + BorderRadius.circular(8.0), + child: item.thumbnail.isNotEmpty + ? Image.network( + item.thumbnail, + fit: BoxFit.cover, + errorBuilder: (context, error, + stackTrace) { + return Container( + color: Colors.grey[200], + child: const Icon( + Icons.broken_image, + size: 40, + color: Colors.grey), + ); + }, + 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, + strokeWidth: 2.0, + ), + ); + }, + ) + : Container( + color: Colors.grey[200], + child: const Icon( + Icons.image_not_supported, + size: 40, + color: Colors.grey), + ), + ), + ), + const SizedBox(height: 6), + Text( + item.lessonTag, + style: const TextStyle( + fontSize: 13, + fontWeight: FontWeight.w500), + maxLines: 2, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ], ), - ); - } else { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text('유효한 YouTube URL이 아닙니다: ${item.lessonUrl}')), - ); - } + ), + ); }, ), - ); - }, + )], ); } },