fix(player): 持久化竖滑播放画面层

This commit is contained in:
Zhou Weidong committed 2026-10-26 15:34:24 +08:00
1 parent 5dafc68775
commit 2a32dec183
5 files changed
+593 -101

No files matched your search

@@ -25,6 +25,9 @@ class PlayerVideoSurface extends StatefulWidget {
this.fallback = PlayerOpeningFallback.black,
this.warmSlotReused = false,
this.waitForPlaybackFrame = false,
this.debugSlot = 'default',
this.debugIndex,
this.showFallbackBackground = true,
});
final VideoPlayerController? controller;
@@ -49,6 +52,9 @@ class PlayerVideoSurface extends StatefulWidget {
/// 自动播放中的新集等待 position 首次前进后再移交画面。
final bool waitForPlaybackFrame;
final String debugSlot;
final int? debugIndex;
final bool showFallbackBackground;
final String videoUrl;
final bool theater;
@@ -79,8 +85,11 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
final value = widget.controller?.value;
_updatePlaybackFlags(value);
_videoSize = value != null && value.isInitialized ? value.size : null;
_logSlot('init', value);
if (_firstFrameConfirmed(value)) {
_confirmFirstFrame(initial: true);
} else {
_logLayer('placeholder', value);
}
}
@@ -88,13 +97,19 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
void didUpdateWidget(covariant PlayerVideoSurface oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.controller != widget.controller) {
_logSlot('controller-change-before', oldWidget.controller?.value);
_detachController(oldWidget.controller);
_resetForControllerChange();
_attachController(widget.controller);
final value = widget.controller?.value;
_updatePlaybackFlags(value);
_videoSize = value != null && value.isInitialized ? value.size : null;
if (_firstFrameConfirmed(value)) _confirmFirstFrame(initial: true);
_logSlot('controller-change-after', value);
if (_firstFrameConfirmed(value)) {
_confirmFirstFrame(initial: true);
} else {
_logLayer('placeholder', value);
}
return;
}
if (!_transitioning &&
@@ -119,6 +134,7 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
@override
void dispose() {
_logSlot('dispose', widget.controller?.value);
_detachController(widget.controller);
_crossFade
..removeStatusListener(_handleFadeStatus)
@@ -143,10 +159,11 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
return Stack(
fit: StackFit.expand,
children: [
const ColoredBox(
key: Key('player-fallback-background'),
color: AppColors.mediaBlack,
),
if (widget.showFallbackBackground)
const ColoredBox(
key: Key('player-fallback-background'),
color: AppColors.mediaBlack,
),
if (showPlaceholder)
KeyedSubtree(
key: const Key('player-single-placeholder'),
@@ -210,14 +227,16 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
_confirmationScheduled = true;
if (kDebugMode) {
debugPrint(
'[player-layer] ts=${DateTime.now().microsecondsSinceEpoch} '
'[player-layer] ts=${DateTime.now().millisecondsSinceEpoch} '
'slot=${widget.debugSlot} index=${widget.debugIndex ?? '-'} '
'phase=first-frame-scheduled',
);
}
WidgetsBinding.instance.addPostFrameCallback((_) {
if (kDebugMode) {
debugPrint(
'[player-layer] ts=${DateTime.now().microsecondsSinceEpoch} '
'[player-layer] ts=${DateTime.now().millisecondsSinceEpoch} '
'slot=${widget.debugSlot} index=${widget.debugIndex ?? '-'} '
'phase=first-frame-callback mounted=$mounted',
);
}
@@ -246,6 +265,7 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
void _confirmFirstFrame({bool initial = false}) {
if (_frameConfirmed || _videoSize == null) return;
_frameConfirmed = true;
_logSlot('firstFrameRendered', widget.controller?.value);
final canSkipPlaceholder =
widget.warmFrameAvailable || _placeholderSnapshot == null;
if (canSkipPlaceholder) {
@@ -303,6 +323,8 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
? 'video'
: _fallbackLabel(widget.fallback);
final signature =
'slot=${widget.debugSlot} '
'index=${widget.debugIndex ?? '-'} '
'phase=$phase warm=${widget.warmFrameAvailable ? 1 : 0} '
'reusedWarm=${widget.warmSlotReused ? 1 : 0} '
'frame=${_frameConfirmed ? 1 : 0} '
@@ -312,14 +334,34 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
'init=${value?.isInitialized == true ? 1 : 0} '
'position=${value?.position.inMilliseconds ?? 0} '
'playing=${value?.isPlaying == true ? 1 : 0} '
'buffering=${value?.isBuffering == true ? 1 : 0}';
'buffering=${value?.isBuffering == true ? 1 : 0} '
'placeholder=${_placeholderLabel()}';
if (signature == _lastLayerState) return;
_lastLayerState = signature;
debugPrint(
'[player-layer] ts=${DateTime.now().microsecondsSinceEpoch} $signature',
'[player-layer] ts=${DateTime.now().millisecondsSinceEpoch} $signature',
);
}
void _logSlot(String phase, VideoPlayerValue? value) {
if (!kDebugMode) return;
debugPrint(
'[player-layer] ts=${DateTime.now().millisecondsSinceEpoch} '
'slot=${widget.debugSlot} index=${widget.debugIndex ?? '-'} '
'phase=$phase init=${value?.isInitialized == true ? 1 : 0}',
);
}
String _placeholderLabel() {
if (_placeholderSnapshot == null) return 'none';
return switch (widget.fallback) {
PlayerOpeningFallback.handoff => 'handoff',
PlayerOpeningFallback.poster => 'poster',
PlayerOpeningFallback.thumbnails => 'thumbnails',
PlayerOpeningFallback.black => 'none',
};
}
String _fallbackLabel(PlayerOpeningFallback fallback) {
if (_placeholderSnapshot == null) return 'black';
return switch (fallback) {