fix(player): 完善进度预览清屏与下拉刷新

This commit is contained in:
Zhou Weidong committed 2026-10-23 03:18:24 +08:00
1 parent 9b1afbdb36
commit f8500bbaca
15 files changed
+949 -251

No files matched your search

+58 -20
View File
@@ -34,6 +34,7 @@ import 'player_logic.dart';
import 'widgets/home_feed_header.dart';
import 'widgets/home_feed_overlay.dart';
import 'widgets/exit_clear_screen_button.dart';
import 'widgets/feed_pull_refresh.dart';
import 'widgets/player_center_toggle.dart';
import 'widgets/player_more_sheet.dart';
import 'widgets/player_overlay.dart';
@@ -49,6 +50,7 @@ part 'player_view_actions.dart';
const double _boostPlaybackSpeed = 2.0;
const double _playerLoadingSize = 24;
const double _playerLoadingStrokeWidth = 2;
const double _feedRefreshTopGap = 56;
class PlayerPage extends StatefulWidget {
const PlayerPage({
@@ -305,6 +307,16 @@ class _PlayerPageState extends State<PlayerPage>
unawaited(logic.setPlaybackSpeed(speed));
}
Future<void> _refreshHomeFeed() async {
final refresh = widget.onRefreshFeed;
if (refresh == null) return;
final ok = await refresh();
if (!mounted || ok) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(AppLocalizations.of(context)!.catalogError)),
);
}
void _setScrubbing(bool value) {
if (!mounted) return;
if (_scrubbing == value) return;
@@ -404,26 +416,37 @@ class _PlayerPageState extends State<PlayerPage>
final interactions = context.watch<InteractionController>();
final l10n = AppLocalizations.of(context)!;
final indices = logic.visibleIndices;
final pages = logic.pages;
final content = AnimatedBuilder(
animation: logic.session,
builder: (context, _) => PageView.builder(
controller: pages,
scrollDirection: Axis.vertical,
allowImplicitScrolling: true,
itemCount: indices.length,
onPageChanged: (position) {
final index = indices[position];
widget.onDramaChanged?.call(logic.queue.entryAt(index).drama);
unawaited(logic.selectEpisode(index));
if (position >= indices.length - 3) {
final loadMore = widget.onLoadMore;
if (loadMore != null) unawaited(loadMore());
}
},
itemBuilder: (context, position) =>
_buildEpisode(context, indices[position], interactions),
),
builder: (context, _) {
final pages = PageView.builder(
controller: logic.pages,
scrollDirection: Axis.vertical,
allowImplicitScrolling: true,
physics: widget.onRefreshFeed == null
? null
: const FeedRefreshPhysics(),
itemCount: indices.length,
onPageChanged: (position) {
final index = indices[position];
widget.onDramaChanged?.call(logic.queue.entryAt(index).drama);
unawaited(logic.selectEpisode(index));
if (position >= indices.length - 3) {
final loadMore = widget.onLoadMore;
if (loadMore != null) unawaited(loadMore());
}
},
itemBuilder: (context, position) =>
_buildEpisode(context, indices[position], interactions),
);
if (widget.onRefreshFeed == null) return pages;
return FeedPullRefresh(
pageController: logic.pages,
topInset: MediaQuery.paddingOf(context).top + _feedRefreshTopGap,
onRefresh: _refreshHomeFeed,
child: pages,
);
},
);
final body = Stack(
fit: StackFit.expand,
@@ -504,9 +527,17 @@ class _PlayerPageState extends State<PlayerPage>
final sessionEpisode = index >= 0 && index < logic.sessionEpisodes.length
? logic.sessionEpisodes[index]
: entry.episode;
final episode = widget.homeMode
final displayed = widget.homeMode
? logic.displayEpisode(drama, sessionEpisode)
: sessionEpisode;
final episode =
(displayed.thumbnailsUrl == null || displayed.thumbnailsUrl!.isEmpty) &&
sessionEpisode.thumbnailsUrl != null
? displayed.withVideoUrl(
displayed.videoUrl,
thumbnailsUrl: sessionEpisode.thumbnailsUrl,
)
: displayed;
final selected = index == logic.session.currentIndex;
// Selection changes before the prepared slot is promoted. Use its controller
// throughout the handoff instead of briefly falling back to a placeholder.
@@ -702,6 +733,7 @@ class _PlayerPageState extends State<PlayerPage>
thumbnailService: _thumbnailService,
spriteAxisService: _spriteAxisService,
),
scrubbing: _scrubbing,
actions: PlayerOverlayActions(
onBack: widget.embedded
? null
@@ -719,7 +751,13 @@ class _PlayerPageState extends State<PlayerPage>
onSave: () => unawaited(_toggleSave(context, drama)),
onShare: () => logic.shareDrama(context, drama),
onMore: () => _openMore(context, episode),
onScrubStart: ready ? () => logic.beginScrub(videoPort!) : null,
onScrubStart: ready
? () {
_setScrubbing(true);
logic.beginScrub(videoPort!);
}
: null,
onScrubEnd: () => _setScrubbing(false),
onSeek: ready
? (position) => logic.seekTo(context, videoPort!, position)
: null,