novyronst/lib/pages/first/player_detail/video_esp_page.dart

218 lines
6.3 KiB
Dart

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<NyEpisodeModel>? onSelectedEpisode;
@override
State<VideoEspPage> createState() => _VideoEspPageState();
}
class _VideoEspPageState extends State<VideoEspPage> {
late int _selectedEpisode;
NyDramaModel? get _drama =>
widget.detail.shortPlayInfo ?? widget.detail.videoInfo;
NyDramaModel? get _info => widget.detail.videoInfo;
List<NyEpisodeModel> 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;
}
}