import 'package:flutter/material.dart'; import 'package:novyronst/common/const/const_color.dart'; import 'package:novyronst/common/help/help_image.dart'; import 'package:novyronst/common/model/ny_detail_model.dart'; import 'package:novyronst/common/model/ny_drama_model.dart'; import 'package:novyronst/common/model/ny_episode_model.dart'; import 'package:novyronst/tools/util/app_screen.dart'; import 'package:novyronst/tools/widgets/novy_com_widget.dart'; class VideoEspPage extends StatefulWidget { const VideoEspPage({ super.key, required this.detail, this.currentEpisode, this.onSelectedEpisode, }); final NyDetailModel detail; final int? currentEpisode; final ValueChanged? onSelectedEpisode; @override State createState() => _VideoEspPageState(); } class _VideoEspPageState extends State { late int _selectedEpisode; NyDramaModel? get _drama => widget.detail.shortPlayInfo ?? widget.detail.videoInfo; NyDramaModel? get _info => widget.detail.videoInfo; List get _episodes => widget.detail.episodeList; @override void initState() { super.initState(); _selectedEpisode = widget.currentEpisode ?? _drama?.currentEpisode ?? _drama?.episode ?? 1; } @override Widget build(BuildContext context) { const height = 520.0; return Container( width: AppScreen.screenWidth, height: height, decoration: const BoxDecoration( color: Colors.white, borderRadius: BorderRadius.vertical(top: Radius.circular(18)), ), child: SafeArea( top: false, child: Padding( padding: const EdgeInsets.fromLTRB(22, 24, 22, 24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildDramaInfo(), const SizedBox(height: 12), Divider( height: 1, thickness: 0.5, color: Colors.black.withValues(alpha: 0.08), ), const SizedBox(height: 12), NovyText( '$_episodeTotal EP', color: Colors.black, fontSize: 15, fontWeight: FontWeight.w500, ), const SizedBox(height: 8), Expanded(child: _buildEpisodeGrid()), ], ), ), ), ); } Widget _buildDramaInfo() { final drama = _drama; final imageUrl = drama?.imageUrl ?? ''; return SizedBox( height: 130, child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ HelpImage.networkRadius( imageUrl, width: 96, height: 130, fit: BoxFit.cover, borderRadius: BorderRadius.circular(2), ), const SizedBox(width: 16), Expanded( child: Padding( padding: const EdgeInsets.only(top: 7), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ NovyText( drama?.name ?? '', color: Colors.black, fontSize: 18, fontWeight: FontWeight.w700, maxLines: 2, lineHeight: 1.12, ), const SizedBox(height: 10), NovyText( drama?.description ?? _info?.description ?? '', color: Colors.black.withValues(alpha: 0.46), fontSize: 14, maxLines: 2, lineHeight: 1.45, ), const SizedBox(height: 13), _buildTag(), ], ), ), ), ], ), ); } Widget _buildTag() { final categoryList = _drama?.categoryList; final name = categoryList?.isNotEmpty == true ? categoryList!.first.name : (_drama?.tagType ?? ''); if (name.isEmpty) return const SizedBox.shrink(); return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: const Color(0xFFE7FFE5), border: Border.all(color: const Color(0xFF86D57A), width: 1), ), child: NovyText( name, color: Colors.black, fontSize: 14, overflow: TextOverflow.visible, ), ); } Widget _buildEpisodeGrid() { if (_episodes.isEmpty) return const SizedBox.shrink(); return GridView.builder( padding: EdgeInsets.zero, physics: const BouncingScrollPhysics(), itemCount: _episodes.length, gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 5, mainAxisSpacing: 10, crossAxisSpacing: 10, childAspectRatio: 61 / 50, ), itemBuilder: (context, index) { return _buildEpisodeItem(_episodes[index]); }, ); } Widget _buildEpisodeItem(NyEpisodeModel episode) { final selected = episode.episode == _selectedEpisode; final selectedPlayable = selected && !episode.isLock; final selectedLocked = selected && episode.isLock; return GestureDetector( behavior: HitTestBehavior.opaque, onTap: () { if (_selectedEpisode == episode.episode) return; setState(() { _selectedEpisode = episode.episode; }); widget.onSelectedEpisode?.call(episode); }, child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: selectedPlayable ? ConstColor.btGreen : const Color(0xFFF8F8F8), borderRadius: BorderRadius.circular(12), border: Border.all( color: selectedPlayable ? Colors.black : (selectedLocked ? ConstColor.btGreen : Colors.transparent), width: selected ? 1 : 0, ), ), child: NovyText( episode.episode.toString(), color: Colors.black.withValues(alpha: 0.86), fontSize: 20, fontWeight: selected ? FontWeight.w700 : FontWeight.w400, ), ), ); } int get _episodeTotal { final total = _drama?.episodeTotal ?? 0; return total > 0 ? total : _episodes.length; } }