fix(player): 修复返回首页黑屏并恢复预热
This commit is contained in:
1 parent
6667330ce0
commit
54eb5a60a8
9 files changed
+56
-101
No files matched your search
@@ -2,6 +2,7 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:video_player/video_player.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
@@ -221,30 +222,36 @@ class PlayerLogic extends ChangeNotifier {
|
||||
/// 独立播放页覆盖首页时只暂停并保存进度,首页 session 与 controller 保持存活。
|
||||
Future<void> pauseForFullPlayer() async {
|
||||
if (!_started) return;
|
||||
_logFeedHandoff('pause');
|
||||
cancelScrub();
|
||||
await session.pauseForRouteHandoff();
|
||||
_logFeedHandoff('paused');
|
||||
await saveCurrentProgress();
|
||||
}
|
||||
|
||||
/// 返回首页前把仍存活的 feed controller 定位到第 1 集起点。
|
||||
Future<void> prepareFeedResume() async {
|
||||
if (!_started) return;
|
||||
await session.playbackSettled;
|
||||
final player = session.active;
|
||||
if (player is VideoPlayerPort && player.controller.value.isInitialized) {
|
||||
await player.seekTo(Duration.zero);
|
||||
}
|
||||
}
|
||||
|
||||
/// 独立播放页关闭并恢复首页路由后,等原生播放器实际恢复再结束交接。
|
||||
Future<void> resumeAfterFullPlayer() async {
|
||||
if (!_started) return;
|
||||
_logFeedHandoff('resume');
|
||||
if (visibleIndices.isNotEmpty) {
|
||||
await session.resumeForRouteHandoff();
|
||||
} else {
|
||||
session.setRouteVisible(false);
|
||||
await session.playbackSettled;
|
||||
}
|
||||
_logFeedHandoff('resumed');
|
||||
}
|
||||
|
||||
void _logFeedHandoff(String phase) {
|
||||
if (!kDebugMode) return;
|
||||
final player = session.active;
|
||||
final video = player is VideoPlayerPort ? player : null;
|
||||
debugPrint(
|
||||
'[feed-session] phase=$phase session=${identityHashCode(session)} '
|
||||
'index=${session.currentIndex} episode=${session.currentEpisode.id} '
|
||||
'controller=${video == null ? '-' : identityHashCode(video.controller)} '
|
||||
'live=${session.livePlayerCount}',
|
||||
);
|
||||
}
|
||||
|
||||
/// 串行替换首页 feed 队列;频道变化时重置到第 1 个,刷新时尽量保留当前剧。
|
||||
@@ -283,7 +290,6 @@ class PlayerLogic extends ChangeNotifier {
|
||||
activate: activatePlayer,
|
||||
coordinator: coordinator,
|
||||
waitForFrame: _waitForFrame,
|
||||
warmNeighborsEnabled: !homeMode,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1230,7 +1236,6 @@ class PlayerLogic extends ChangeNotifier {
|
||||
),
|
||||
);
|
||||
if (!isMounted()) return;
|
||||
await prepareFeedResume();
|
||||
await resumeAfterFullPlayer();
|
||||
}
|
||||
|
||||
|
||||
@@ -57,12 +57,8 @@ const double _playerLoadingStrokeWidth = 2;
|
||||
const double _feedRefreshTopGap = 56;
|
||||
|
||||
class PlayerPageHandoff {
|
||||
PlayerPageHandoff({
|
||||
required this.prepareSource,
|
||||
required this.sourceUserWantsPlay,
|
||||
});
|
||||
PlayerPageHandoff({required this.sourceUserWantsPlay});
|
||||
|
||||
final Future<void> Function() prepareSource;
|
||||
final bool sourceUserWantsPlay;
|
||||
}
|
||||
|
||||
@@ -145,8 +141,6 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
DateTime? _swipeStartedAt;
|
||||
bool _swipePrewarmCaptured = false;
|
||||
bool _presentationActive = false;
|
||||
bool _presentationSuspended = false;
|
||||
bool _presentationReturning = false;
|
||||
bool _leavingHandoff = false;
|
||||
bool _preparingFullPlayer = false;
|
||||
final Set<String> _precachedPosterUrls = {};
|
||||
@@ -538,8 +532,6 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
}
|
||||
|
||||
Widget _buildPlayer(BuildContext context) {
|
||||
if (_presentationSuspended) return const SizedBox.expand();
|
||||
if (_presentationReturning) return _buildPresentationReturnLayer();
|
||||
final interactions = context.watch<InteractionController>();
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final indices = logic.visibleIndices;
|
||||
@@ -736,17 +728,6 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
};
|
||||
}
|
||||
|
||||
Widget _buildPresentationReturnLayer() {
|
||||
final indices = logic.visibleIndices;
|
||||
return AnimatedBuilder(
|
||||
animation: logic.session,
|
||||
builder: (context, _) => PlayerRenderStack(
|
||||
pageController: _pageController,
|
||||
slots: _renderSlots(indices),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
PlayerRenderSlotData _renderSlot(
|
||||
PlayerRenderSlotRole role,
|
||||
int? index,
|
||||
@@ -1191,10 +1172,7 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
_preparingFullPlayer = false;
|
||||
}
|
||||
if (!mounted || !context.mounted) return;
|
||||
final handoff = PlayerPageHandoff(
|
||||
prepareSource: _prepareSourceAfterFullPlayer,
|
||||
sourceUserWantsPlay: sourceUserWantsPlay,
|
||||
);
|
||||
final handoff = PlayerPageHandoff(sourceUserWantsPlay: sourceUserWantsPlay);
|
||||
_logFullPlayerRoute('present');
|
||||
setState(() => _presentationActive = true);
|
||||
final route = Navigator.of(context).push<void>(
|
||||
@@ -1208,41 +1186,19 @@ class _PlayerPageState extends State<PlayerPage>
|
||||
),
|
||||
),
|
||||
);
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
if (mounted) {
|
||||
setState(() => _presentationSuspended = true);
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
}
|
||||
await route;
|
||||
if (!mounted) return;
|
||||
logic.attach(widget, isMounted: () => mounted);
|
||||
setState(() {
|
||||
_presentationActive = false;
|
||||
_presentationSuspended = false;
|
||||
_presentationReturning = false;
|
||||
});
|
||||
setState(() => _presentationActive = false);
|
||||
await logic.resumeAfterFullPlayer();
|
||||
if (!mounted) return;
|
||||
_logFullPlayerRoute('restored');
|
||||
}
|
||||
|
||||
Future<void> _prepareSourceAfterFullPlayer() async {
|
||||
if (!mounted) return;
|
||||
logic.attach(widget, isMounted: () => mounted);
|
||||
await logic.prepareFeedResume();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_presentationSuspended = false;
|
||||
_presentationReturning = true;
|
||||
});
|
||||
await WidgetsBinding.instance.endOfFrame;
|
||||
}
|
||||
|
||||
Future<void> _leaveHandoff(BuildContext context) async {
|
||||
if (_leavingHandoff) return;
|
||||
setState(() => _leavingHandoff = true);
|
||||
await logic.session.pauseForRouteHandoff();
|
||||
await widget.handoff?.prepareSource();
|
||||
if (context.mounted) Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
|
||||
@@ -348,7 +348,9 @@ class _PlayerVideoSurfaceState extends State<PlayerVideoSurface>
|
||||
debugPrint(
|
||||
'[player-layer] ts=${DateTime.now().millisecondsSinceEpoch} '
|
||||
'slot=${widget.debugSlot} index=${widget.debugIndex ?? '-'} '
|
||||
'phase=$phase init=${value?.isInitialized == true ? 1 : 0}',
|
||||
'phase=$phase state=${identityHashCode(this)} '
|
||||
'controller=${widget.controller == null ? '-' : identityHashCode(widget.controller)} '
|
||||
'init=${value?.isInitialized == true ? 1 : 0}',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user