feat: 视频详情页,分类页。个人中心
This commit is contained in:
1 parent
b5bce6edcf
commit
a3426831ed
26 files changed
+2878
-1077
No files matched your search
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:novyronst/common/api/video_request.dart';
|
||||
import 'package:novyronst/common/model/ny_drama_model.dart';
|
||||
import 'package:novyronst/pages/play/widgets/player_view.dart';
|
||||
import 'package:novyronst/pages/widgets/ny_loading_widget.dart';
|
||||
|
||||
class PlayPage extends StatefulWidget {
|
||||
const PlayPage({super.key, this.isActive = true});
|
||||
@@ -69,33 +70,26 @@ class _RootPageState extends State<PlayPage> {
|
||||
if (index < 0 || index >= _videoList.length) return;
|
||||
|
||||
final item = _videoList[index];
|
||||
final videoId = item.shortPlayVideoId ?? item.id;
|
||||
if (videoId == null) return;
|
||||
final _info = item.videoInfo ?? {};
|
||||
final videoId = _info['short_play_video_id'];
|
||||
if (videoId == null) {
|
||||
return;
|
||||
}
|
||||
final shortPlayId = _info['short_play_id'];
|
||||
if (shortPlayId == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shortPlayId = item.shortPlayId ?? item.shortId;
|
||||
final oldIsCollect = item.isCollect ?? false;
|
||||
final oldCollectTotal = item.collectTotal;
|
||||
final newIsCollect = !oldIsCollect;
|
||||
final newCollectTotal = oldCollectTotal + (newIsCollect ? 1 : -1);
|
||||
final success = item.isCollect == false
|
||||
? await VideoRequest.collect(shortPlayId: shortPlayId ?? 0, videoId: videoId)
|
||||
: await VideoRequest.cancelCollect(shortPlayId: shortPlayId ?? 0);
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_videoList[index] = item.copyWith(
|
||||
isCollect: newIsCollect,
|
||||
collectTotal: newCollectTotal < 0 ? 0 : newCollectTotal,
|
||||
);
|
||||
});
|
||||
|
||||
final success = newIsCollect
|
||||
? await VideoRequest.collect(shortPlayId: shortPlayId, videoId: videoId)
|
||||
: await VideoRequest.cancelCollect(shortPlayId: shortPlayId);
|
||||
|
||||
if (!mounted || success) return;
|
||||
|
||||
setState(() {
|
||||
_videoList[index] = _videoList[index].copyWith(
|
||||
isCollect: oldIsCollect,
|
||||
collectTotal: oldCollectTotal,
|
||||
);
|
||||
_videoList[index] = item.copyWith(isCollect: !item.isCollect!);
|
||||
// item.isCollect = !item.isCollect!;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -106,9 +100,7 @@ class _RootPageState extends State<PlayPage> {
|
||||
|
||||
Widget _buildBody() {
|
||||
if (_isLoading && _videoList.isEmpty) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(color: Colors.white),
|
||||
);
|
||||
return const NyLoadingWidget();
|
||||
}
|
||||
|
||||
if (_videoList.isEmpty) {
|
||||
|
||||
@@ -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