feat: 视频详情页,分类页。个人中心
This commit is contained in:
1 parent
b5bce6edcf
commit
a3426831ed
26 files changed
+2878
-1077
No files matched your search
@@ -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;
|
||||
}
|
||||
Reference in new issue
Block a user