fix(player): 收敛播放页入口加载态

This commit is contained in:
Zhou Weidong committed 2026-10-26 17:56:40 +08:00
1 parent ee4e573f4f
commit b291044acc
7 files changed
+164 -90

No files matched your search

+55 -24
View File
@@ -142,11 +142,11 @@ class _PlayerPageState extends State<PlayerPage>
bool _presentationActive = false;
bool _leavingHandoff = false;
bool _preparingFullPlayer = false;
int? _loadingIndex;
int? _videoReadyIndex;
bool _loadingVisible = false;
bool _loadingRemoved = true;
bool _loadingFadeScheduled = false;
bool _loggedLoadingUiVisible = false;
final Set<String> _precachedPosterUrls = {};
/// 收藏切换后的本地增量:基准是详情快照,切换成功按服务端状态 ±1。
@@ -179,10 +179,16 @@ class _PlayerPageState extends State<PlayerPage>
initialUserWantsPlay: handoff?.sourceUserWantsPlay ?? true,
);
_pageController = logic.pages;
if (!widget.embedded) {
_loadingIndex = logic.session.currentIndex;
if (!widget.embedded &&
!logic.isEpisodeLocked(logic.session.currentEpisode)) {
_loadingVisible = true;
_loadingRemoved = false;
if (kDebugMode) {
debugPrint(
'[player-entry] phase=loading-shown-once '
'index=${logic.session.currentIndex}',
);
}
}
}
@@ -352,6 +358,15 @@ class _PlayerPageState extends State<PlayerPage>
notification.dragDetails != null) {
_swipeStartedAt = DateTime.now();
_swipePrewarmCaptured = false;
if (!widget.embedded && !_loadingRemoved) {
setState(() {
_loadingVisible = false;
_loadingRemoved = true;
});
if (kDebugMode) {
debugPrint('[player-entry] phase=loading-dismissed-on-swipe');
}
}
} else if (notification is ScrollUpdateNotification &&
!_swipePrewarmCaptured &&
notification.dragDetails != null) {
@@ -544,14 +559,12 @@ class _PlayerPageState extends State<PlayerPage>
final interactions = context.watch<InteractionController>();
final l10n = AppLocalizations.of(context)!;
final indices = logic.visibleIndices;
final currentIndex = logic.session.currentIndex;
if (!widget.embedded && _loadingIndex != currentIndex) {
_loadingIndex = currentIndex;
_videoReadyIndex = null;
_loadingVisible = true;
_loadingRemoved = false;
_loadingFadeScheduled = false;
}
final activePort = logic.session.active;
final activeVideo = activePort is VideoPlayerPort ? activePort : null;
final canWaitForVideoFrame =
activeVideo != null &&
activeVideo.controller.value.isInitialized &&
!activeVideo.controller.value.hasError;
final entryUnavailable =
logic.ticketError != null ||
logic.blockedEpisodeId != null ||
@@ -560,10 +573,10 @@ class _PlayerPageState extends State<PlayerPage>
if (!widget.embedded &&
!_loadingRemoved &&
!_loadingFadeScheduled &&
entryUnavailable) {
entryUnavailable &&
!canWaitForVideoFrame) {
_scheduleLoadingFade('entry-unavailable');
}
final activePort = logic.session.active;
if (!widget.embedded &&
!_loadingRemoved &&
!_loadingFadeScheduled &&
@@ -631,8 +644,17 @@ class _PlayerPageState extends State<PlayerPage>
pageController: _pageController,
slots: _renderSlots(indices),
onCurrentVideoVisible: _handleCurrentVideoVisible,
showNeighbors: widget.embedded || _loadingRemoved,
),
if (!widget.embedded && !_loadingRemoved)
Center(
child: IgnorePointer(
key: const Key('player-entry-loading-overlay'),
child: PlayerLoadingOverlay(
opacity: _loadingVisible ? 1 : 0,
onFadeEnd: _handleLoadingFadeEnd,
),
),
),
pageLayer,
],
);
@@ -675,14 +697,6 @@ class _PlayerPageState extends State<PlayerPage>
),
),
),
if (!widget.embedded && !_loadingRemoved)
Positioned.fill(
child: PlayerLoadingOverlay(
key: ValueKey('player-loading-${_loadingIndex ?? currentIndex}'),
opacity: _loadingVisible ? 1 : 0,
onFadeEnd: _handleLoadingFadeEnd,
),
),
],
);
final player = AnnotatedRegion<SystemUiOverlayStyle>(
@@ -717,7 +731,7 @@ class _PlayerPageState extends State<PlayerPage>
}
void _handleCurrentVideoVisible(int? index) {
if (!mounted) return;
if (!mounted || _loadingRemoved) return;
final currentIndex = logic.session.currentIndex;
if (index == null ||
index != currentIndex ||
@@ -733,6 +747,14 @@ class _PlayerPageState extends State<PlayerPage>
}
WidgetsBinding.instance.addPostFrameCallback((_) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted ||
_loadingRemoved ||
index != logic.session.currentIndex) {
return;
}
if (kDebugMode) {
debugPrint('[player-entry] phase=video-frame-painted index=$index');
}
_startLoadingFade(currentIndex);
});
});
@@ -938,11 +960,20 @@ class _PlayerPageState extends State<PlayerPage>
if (selected && videoReady) _logSwipeReveal();
final ready = !locked && (videoReady || port != null);
final theater = logic.pip || logic.pipPreparing;
final entryLoading = !widget.embedded && !_loadingRemoved;
if (kDebugMode && entryLoading && selected && !_loggedLoadingUiVisible) {
_loggedLoadingUiVisible = true;
debugPrint(
'[player-entry] phase=loading-ui-visible '
'index=$index top=1 rail=1 progress=1',
);
}
return Stack(
fit: StackFit.expand,
children: [
if (selected &&
!entryLoading &&
!locked &&
!theater &&
!ready &&
@@ -962,7 +993,7 @@ class _PlayerPageState extends State<PlayerPage>
),
),
// 票据三态之一:起播前正在向后端要签名地址
if (selected && !theater && logic.ticketLoading)
if (selected && !entryLoading && !theater && logic.ticketLoading)
const Center(
child: SizedBox.square(
dimension: _playerLoadingSize,