From fc445e7c808e3a2532a876b53ccbd61030728ac8 Mon Sep 17 00:00:00 2001 From: csw <471155679@qq.com> Date: Wed, 1 Jul 2026 18:05:57 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E9=97=AE=E9=A2=98=E5=A4=84=E7=90=86~?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/home_page.dart | 34 ++++- .../home/players/page/drama_detail_page.dart | 142 ++++++++++++++++-- .../home/players/widget/sky_player_view.dart | 62 ++++++-- lib/pages/mine/page/mine_collection_page.dart | 4 +- lib/pages/search/page/result_page.dart | 2 +- 5 files changed, 206 insertions(+), 38 deletions(-) diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart index f03273d..76ccd2a 100644 --- a/lib/pages/home/home_page.dart +++ b/lib/pages/home/home_page.dart @@ -71,21 +71,39 @@ class _MainRootState extends State { child: SafeArea( child: _homeData.isEmpty ? const SizedBox() - : EasyRefresh( - controller: _refreshController, - header: const GifHeader(), - onRefresh: _refreshHome, - child: _bodyWidget(), - ), + : _newBodyWidget(), ), ); } + Widget _newBodyWidget() { + return Column( + children: [ + Container( + height: 204.h, + padding: EdgeInsets.only(left: 25.w, top: 3.h, right: 25.w), + child: HelpImg.asset(ConstImg.homeTopBg, fit: BoxFit.fill), + ).addInkWell(onTap: () { + // 去到搜索页面 + HelpNav.to(ResultPage(keyword: '')); + }), + const SizedBox(height: 10), + Expanded(child: EasyRefresh( + controller: _refreshController, + header: const GifHeader(), + onRefresh: _refreshHome, + child: _bodyWidget(), + )) + ], + ); + } + Widget _bodyWidget() { return CustomScrollView( slivers: [ - _buildTopTwoImgs(), - _buildSpace(), + // _buildTopTwoImgs(), + // _buildSpace(), + _buildTitleImg(ConstImg.homeTitleDaily), DailyQuestWidget( dramaList: _homeData .firstWhere((element) => element.moduleKey == 'home_v3_recommand') diff --git a/lib/pages/home/players/page/drama_detail_page.dart b/lib/pages/home/players/page/drama_detail_page.dart index c45f76a..49fdcd2 100644 --- a/lib/pages/home/players/page/drama_detail_page.dart +++ b/lib/pages/home/players/page/drama_detail_page.dart @@ -22,6 +22,9 @@ class DramaDetailPage extends StatefulWidget { class _DramaDetailPageState extends State { SkywoodDetail? _detailBean; SkywoodEpisode? _selectedEpisode; + final PageController _pageController = PageController(); + int _currentPageIndex = 0; + int? _requestingVideoId; @override void initState() { @@ -33,33 +36,120 @@ class _DramaDetailPageState extends State { ); } + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + void _getDetailDrama({ required int shortPlayId, required int shortPlayVideoId, + bool showLoading = true, }) { - AppToast.showLottieLoading(); + _requestingVideoId = shortPlayVideoId; + if (showLoading) { + AppToast.showLottieLoading(); + } PlayClient.getVideoDetail( shortPlayId: shortPlayId, shortPlayVideoId: shortPlayVideoId, ).then((value) { - AppToast.dismiss(); + if (showLoading) { + AppToast.dismiss(); + } if (!mounted) return; + if (_requestingVideoId != shortPlayVideoId) return; setState(() { _detailBean = value; + if (value != null) { + _selectedEpisode ??= _resolveCurrentEpisode(value); + _currentPageIndex = _indexOfEpisode(_selectedEpisode); + } }); + _jumpToCurrentPage(); }); } void _onSelectedEpisode(SkywoodEpisode episode) { setState(() { _selectedEpisode = episode; + _currentPageIndex = _indexOfEpisode(episode); + }); + _jumpToCurrentPage(); + _getDetailDrama( + shortPlayId: episode.shortPlayId, + shortPlayVideoId: episode.shortPlayVideoId, + showLoading: false, + ); + } + + void _onPageChanged(int index) { + final episodes = _detailBean?.episodeList ?? []; + if (index < 0 || index >= episodes.length) return; + + final episode = episodes[index]; + if (_selectedEpisode?.shortPlayVideoId == episode.shortPlayVideoId) { + _currentPageIndex = index; + return; + } + + setState(() { + _selectedEpisode = episode; + _currentPageIndex = index; }); _getDetailDrama( shortPlayId: episode.shortPlayId, shortPlayVideoId: episode.shortPlayVideoId, + showLoading: false, ); } + SkywoodEpisode? _resolveCurrentEpisode(SkywoodDetail detail) { + if (detail.episodeList.isEmpty) return null; + + final int? episode = + _selectedEpisode?.episode ?? + detail.videoInfo?.episode ?? + detail.videoInfo?.currentEpisode ?? + detail.shortPlayInfo?.episode ?? + detail.shortPlayInfo?.currentEpisode ?? + widget.dramaBean.currentEpisode ?? + widget.dramaBean.episode; + + if (episode != null && episode > 0) { + for (final item in detail.episodeList) { + if (item.episode == episode) return item; + } + } + + return detail.episodeList.first; + } + + int _indexOfEpisode(SkywoodEpisode? episode) { + final episodes = _detailBean?.episodeList ?? []; + if (episode == null || episodes.isEmpty) return 0; + + final index = episodes.indexWhere( + (item) => item.shortPlayVideoId == episode.shortPlayVideoId, + ); + return index < 0 ? 0 : index; + } + + void _jumpToCurrentPage() { + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted || !_pageController.hasClients) return; + final episodes = _detailBean?.episodeList ?? []; + if (episodes.isEmpty) return; + final page = _currentPageIndex.clamp(0, episodes.length - 1); + if ((_pageController.page ?? _pageController.initialPage).round() == + page) { + return; + } + _pageController.jumpToPage(page); + }); + } + @override Widget build(BuildContext context) { return Scaffold(backgroundColor: Colors.black, body: _bodyWidget()); @@ -76,17 +166,7 @@ class _DramaDetailPageState extends State { left: 0, right: 0, top: 0, - child: SkyPlayerView( - key: ValueKey( - _selectedEpisode?.shortPlayVideoId ?? - _detailBean?.videoInfo?.shortPlayVideoId ?? - widget.dramaBean.shortPlayVideoId, - ), - detailBean: _detailBean, - dramaBean: widget.dramaBean, - currentEpisode: _selectedEpisode?.episode, - onSelectedEpisode: _onSelectedEpisode, - ), + child: _buildPlayerPager(), ), Positioned( @@ -99,6 +179,42 @@ class _DramaDetailPageState extends State { ); } + Widget _buildPlayerPager() { + final detail = _detailBean!; + final episodes = detail.episodeList; + + if (episodes.isEmpty) { + return SkyPlayerView( + detailBean: detail, + dramaBean: widget.dramaBean, + currentEpisode: _selectedEpisode?.episode, + onSelectedEpisode: _onSelectedEpisode, + ); + } + + return PageView.builder( + controller: _pageController, + scrollDirection: Axis.vertical, + onPageChanged: _onPageChanged, + itemCount: episodes.length, + itemBuilder: (context, index) { + final episode = episodes[index]; + final isCurrent = index == _currentPageIndex; + + return SkyPlayerView( + key: ValueKey( + '${episode.shortPlayVideoId}-$isCurrent-${detail.videoInfo?.shortPlayVideoId}', + ), + autoPlay: isCurrent, + detailBean: detail, + dramaBean: widget.dramaBean, + currentEpisode: episode.episode, + onSelectedEpisode: _onSelectedEpisode, + ); + }, + ); + } + Widget _headerWidget() { return Container( height: 40, diff --git a/lib/pages/home/players/widget/sky_player_view.dart b/lib/pages/home/players/widget/sky_player_view.dart index 990e088..e6db341 100644 --- a/lib/pages/home/players/widget/sky_player_view.dart +++ b/lib/pages/home/players/widget/sky_player_view.dart @@ -59,6 +59,17 @@ class _VideoPlayerWidgetState extends State { oldWidget.currentEpisode != widget.currentEpisode) { _disposeController(); _initializePlayer(); + return; + } + + if (oldWidget.autoPlay != widget.autoPlay && + _controllerCreated && + _isInitialized) { + if (widget.autoPlay) { + _controller.play(); + } else { + _controller.pause(); + } } } @@ -70,6 +81,7 @@ class _VideoPlayerWidgetState extends State { _currentPosition = Duration.zero; _totalDuration = Duration.zero; _isLiked = widget.dramaBean.isCollect ?? false; + _isReportedHis = false; }); _currentEpisode = _resolveCurrentEpisode(); @@ -105,12 +117,14 @@ class _VideoPlayerWidgetState extends State { final videoController = _controller; final position = videoController.value.position; - final duration = videoController.value.duration; - if (_isPlaying && !_isReportedHis && position.inSeconds > 1) { + if (_isPlaying && + !_isReportedHis && + _currentEpisode != null && + position.inSeconds > 1) { _isReportedHis = true; EventClient.eventCreateHis( - shortPlayId: _currentEpisode!.shortPlayId.toString(), - videoId: _currentEpisode!.shortPlayVideoId.toString(), + shortPlayId: _currentEpisode!.shortPlayId.toString(), + videoId: _currentEpisode!.shortPlayVideoId.toString(), ); } } @@ -380,12 +394,12 @@ class _VideoPlayerWidgetState extends State { return SizedBox( width: double.infinity, - height: 3, + height: 28, child: SliderTheme( data: SliderThemeData( trackHeight: 3, // ✅ 进度条高度 3 thumbShape: _ImageThumbShape(imagePath: ConstImg.playTag), - overlayShape: const RoundSliderOverlayShape(overlayRadius: 3), + overlayShape: const RoundSliderOverlayShape(overlayRadius: 10), activeTrackColor: Colors.white, // ✅ 已播放 = 白色 inactiveTrackColor: Colors.white.withAlpha(126), // ✅ 未播放 = 灰色 thumbColor: Colors.white, @@ -396,28 +410,46 @@ class _VideoPlayerWidgetState extends State { value: progress, min: 0, max: 1, - onChangeStart: (_) { - _isSeeking = true; + onChangeStart: (value) { + if (totalMilliseconds <= 0) return; + setState(() { + _isSeeking = true; + _currentPosition = _durationFromProgress( + value, + totalMilliseconds, + ); + }); }, onChanged: (value) { + if (totalMilliseconds <= 0) return; setState(() { - _currentPosition = Duration( - milliseconds: (totalMilliseconds * value).round(), + _currentPosition = _durationFromProgress( + value, + totalMilliseconds, ); }); }, onChangeEnd: (value) { - final position = Duration( - milliseconds: (totalMilliseconds * value).round(), - ); - _controller.seekTo(position); - _isSeeking = false; + if (totalMilliseconds <= 0 || !_controllerCreated) return; + final position = _durationFromProgress(value, totalMilliseconds); + setState(() { + _currentPosition = position; + }); + _controller.seekTo(position).whenComplete(() { + if (!mounted) return; + _isSeeking = false; + _syncPlayerValue(); + }); }, ), ), ); } + Duration _durationFromProgress(double value, int totalMilliseconds) { + return Duration(milliseconds: (totalMilliseconds * value).round()); + } + String _formatDuration(Duration duration) { final minutes = duration.inMinutes.remainder(60).toString().padLeft(2, '0'); final seconds = duration.inSeconds.remainder(60).toString().padLeft(2, '0'); diff --git a/lib/pages/mine/page/mine_collection_page.dart b/lib/pages/mine/page/mine_collection_page.dart index f00772b..96bfe21 100644 --- a/lib/pages/mine/page/mine_collection_page.dart +++ b/lib/pages/mine/page/mine_collection_page.dart @@ -70,7 +70,9 @@ class _CollectionPageState extends State { }, key: Key(item.shortPlayId.toString()), - child: MineItemView(drama: _collectionList[index]), + child: MineItemView(drama: item, onTap: () { + HelpNav.to(DramaDetailPage(dramaBean: item)); + }), ); }, separatorBuilder: (context, index) { diff --git a/lib/pages/search/page/result_page.dart b/lib/pages/search/page/result_page.dart index 7660c66..18c5e36 100644 --- a/lib/pages/search/page/result_page.dart +++ b/lib/pages/search/page/result_page.dart @@ -140,7 +140,7 @@ class ResultItemView extends StatelessWidget { Spacer(), Row( children: [ - _tipView(true, text: '10.0'), + _tipView(true, text: SkyTimeTool.formatNum(drama.collectTotal, unit: 'k')), const SizedBox(width: 15), _tipView(false, text: SkyTimeTool.formatNum(drama.watchTotal, unit: 'k')), ],