feat: 视频详情页,分类页。个人中心

This commit is contained in:
chengsiwen committed 2026-07-07 16:04:15 +08:00
1 parent b5bce6edcf
commit a3426831ed
26 files changed
+2878 -1077

No files matched your search

+53 -5
View File
@@ -13,6 +13,8 @@ class PlayerView extends StatefulWidget {
this.onCollectTap,
this.isLoadingMore = false,
this.isActive = true,
this.bottomMetaSpacing = 76,
this.progressHeaderBuilder,
});
final List<NyDramaModel> videos;
@@ -21,6 +23,9 @@ class PlayerView extends StatefulWidget {
final ValueChanged<int>? onCollectTap;
final bool isLoadingMore;
final bool isActive;
final double bottomMetaSpacing;
final Widget Function(BuildContext context, NyDramaModel drama, int index)?
progressHeaderBuilder;
@override
State<PlayerView> createState() => _PlayerViewState();
@@ -48,6 +53,13 @@ class _PlayerViewState extends State<PlayerView> {
super.didUpdateWidget(oldWidget);
if (widget.videos.isEmpty) return;
if (oldWidget.initialIndex != widget.initialIndex) {
_currentIndex = widget.initialIndex.clamp(0, widget.videos.length - 1);
if (_pageController.hasClients) {
_jumpToPageAfterBuild(_currentIndex);
}
}
if (_currentIndex >= widget.videos.length) {
_currentIndex = widget.videos.length - 1;
}
@@ -187,12 +199,24 @@ class _PlayerViewState extends State<PlayerView> {
}
void _handlePageChanged(int index) {
if (index == _currentIndex) return;
_currentIndex = index;
widget.onPageChanged?.call(index);
_cachePlaybackWindow(index);
widget.isActive ? _playActiveVideo() : _pauseAllVideos();
}
void _jumpToPageAfterBuild(int index) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted || widget.videos.isEmpty || !_pageController.hasClients) {
return;
}
final nextIndex = index.clamp(0, widget.videos.length - 1);
_pageController.jumpToPage(nextIndex);
});
}
void _togglePlayback() {
final controller = _controllers[_currentIndex];
if (!widget.isActive ||
@@ -217,12 +241,15 @@ class _PlayerViewState extends State<PlayerView> {
itemBuilder: (context, index) {
return _PlayerPage(
drama: widget.videos[index],
index: index,
controller: _controllers[index],
isPageActive: widget.isActive,
isLoadingMore:
widget.isLoadingMore && index == widget.videos.length - 1,
bottomMetaSpacing: widget.bottomMetaSpacing,
onTap: _togglePlayback,
onCollectTap: () => widget.onCollectTap?.call(index),
progressHeaderBuilder: widget.progressHeaderBuilder,
);
},
);
@@ -232,23 +259,33 @@ class _PlayerViewState extends State<PlayerView> {
class _PlayerPage extends StatelessWidget {
const _PlayerPage({
required this.drama,
required this.index,
required this.controller,
required this.isPageActive,
required this.isLoadingMore,
required this.bottomMetaSpacing,
required this.onTap,
required this.onCollectTap,
required this.progressHeaderBuilder,
});
final NyDramaModel drama;
final int index;
final VideoPlayerController? controller;
final bool isPageActive;
final bool isLoadingMore;
final double bottomMetaSpacing;
final VoidCallback onTap;
final VoidCallback onCollectTap;
final Widget Function(BuildContext context, NyDramaModel drama, int index)?
progressHeaderBuilder;
@override
Widget build(BuildContext context) {
final bottomPadding = MediaQuery.paddingOf(context).bottom + 86;
final metaBottom = resolvePlayerMetaBottom(
safeAreaBottom: MediaQuery.paddingOf(context).bottom,
bottomSpacing: bottomMetaSpacing,
);
return GestureDetector(
behavior: HitTestBehavior.opaque,
@@ -263,14 +300,14 @@ class _PlayerPage extends StatelessWidget {
Positioned(
left: 16,
right: 16,
bottom: bottomPadding - 10,
child: _buildMeta(),
bottom: metaBottom,
child: _buildMeta(context),
),
if (isLoadingMore)
Positioned(
left: 0,
right: 0,
bottom: bottomPadding - 30,
bottom: metaBottom - 20,
child: const Center(
child: SizedBox(
width: 18,
@@ -324,7 +361,7 @@ class _PlayerPage extends StatelessWidget {
);
}
Widget _buildMeta() {
Widget _buildMeta(BuildContext context) {
final isCollect = drama.isCollect ?? false;
return Column(
mainAxisSize: MainAxisSize.min,
@@ -370,6 +407,10 @@ class _PlayerPage extends StatelessWidget {
],
),
const SizedBox(height: 16),
if (progressHeaderBuilder != null) ...[
progressHeaderBuilder!(context, drama, index),
const SizedBox(height: 8),
],
_buildProgress(),
],
);
@@ -475,3 +516,10 @@ bool shouldPlayActiveVideo(
value.isInitialized &&
!value.isPlaying;
}
double resolvePlayerMetaBottom({
required double safeAreaBottom,
required double bottomSpacing,
}) {
return safeAreaBottom + bottomSpacing;
}