fix(player): 修正缓冲态与首帧时间闪现

This commit is contained in:
Zhou Weidong committed 2026-10-26 21:13:55 +08:00
1 parent 0f27daf664
commit 350d16db75
8 files changed
+262 -22

No files matched your search

+41 -18
View File
@@ -43,6 +43,7 @@ class SeekProgressBar extends StatefulWidget {
this.episodeId,
this.thumbnailsUrl,
this.fallbackDuration = Duration.zero,
this.suppressBufferLoading = false,
});
final ValueListenable<VideoPlayerValue>? valueListenable;
@@ -59,6 +60,7 @@ class SeekProgressBar extends StatefulWidget {
final String? episodeId;
final String? thumbnailsUrl;
final Duration fallbackDuration;
final bool suppressBufferLoading;
@override
State<SeekProgressBar> createState() => _SeekProgressBarState();
@@ -74,6 +76,7 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
bool _pointerMoved = false;
Offset? _pointerStart;
double? _dragStartFraction;
Duration? _lastPosition;
@override
void initState() {
@@ -90,13 +93,22 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
_bufferTimer?.cancel();
_bufferTimer = null;
_buffering = false;
_lastPosition = null;
widget.thumbnailService?.cancelPending();
_attachSource();
}
if (oldWidget.suppressBufferLoading != widget.suppressBufferLoading) {
_bufferTimer?.cancel();
_bufferTimer = null;
_lastPosition = null;
_buffering = false;
_syncBufferState(notify: false);
}
if (oldWidget.locked != widget.locked) {
_bufferTimer?.cancel();
_bufferTimer = null;
_buffering = false;
_lastPosition = null;
if (widget.locked) {
widget.thumbnailService?.cancelPending();
_previewFraction.value = null;
@@ -130,14 +142,24 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
void _syncBufferState({bool notify = true}) {
final source = widget.valueListenable;
if (widget.locked || source == null) {
if (widget.locked || widget.suppressBufferLoading || source == null) {
_bufferTimer?.cancel();
_bufferTimer = null;
_lastPosition = null;
_setBuffering(false, notify: notify);
return;
}
final value = source.value;
if (!value.isInitialized) {
_bufferTimer?.cancel();
_bufferTimer = null;
_lastPosition = null;
_setBuffering(false, notify: notify);
return;
}
final previousPosition = _lastPosition;
_lastPosition = value.position;
if (previousPosition != null && value.position > previousPosition) {
_bufferTimer?.cancel();
_bufferTimer = null;
_setBuffering(false, notify: notify);
@@ -149,14 +171,24 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
_setBuffering(false, notify: notify);
return;
}
// 首次看到“已播放且有画面”的 buffering 状态时,以 position 为基线,
// 不抢先显示 loading;后续 position 停滞超过阈值才视为真实卡顿。
if (previousPosition == null &&
value.isPlaying &&
value.position > Duration.zero) {
_setBuffering(false, notify: notify);
return;
}
if (_buffering || _bufferTimer != null) return;
final bufferingPosition = value.position;
_bufferTimer = Timer(_bufferDebounce, () {
_bufferTimer = null;
final current = widget.valueListenable;
if (!mounted ||
current != source ||
!current!.value.isInitialized ||
!current.value.isBuffering) {
!current.value.isBuffering ||
current.value.position != bufferingPosition) {
return;
}
_setBuffering(true);
@@ -227,7 +259,11 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
: widget.fallbackDuration;
final total = duration.inMilliseconds;
final scrubbing = _dragging;
final loading = !widget.locked && !scrubbing && _buffering;
final loading =
!widget.locked &&
!widget.suppressBufferLoading &&
!scrubbing &&
_buffering;
final enabled =
!widget.locked &&
video.isInitialized &&
@@ -243,6 +279,8 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
final showPreview =
scrubbing &&
widget.showTimes &&
video.isInitialized &&
total > 0 &&
widget.thumbnailService != null &&
(widget.episodeId?.isNotEmpty ?? false);
final trackHeight = scrubbing ? 6.0 : 2.0;
@@ -253,21 +291,6 @@ class _SeekProgressBarState extends State<SeekProgressBar> {
final track = Column(
mainAxisSize: MainAxisSize.min,
children: [
if (widget.showTimes && !video.isInitialized)
Padding(
padding: const EdgeInsets.only(bottom: 2),
child: Text(
'00:00 / ${_format(duration)}',
key: const Key('player-progress-uninitialized-time'),
textAlign: TextAlign.center,
style: const TextStyle(
color: AppColors.onPrimary,
fontSize: 12,
fontWeight: FontWeight.w600,
height: 1.2,
),
),
),
if (widget.showTimes && scrubbing && !showPreview)
Padding(
padding: const EdgeInsets.only(bottom: 2),