fix(player): unify back navigation

close #1525
This commit is contained in:
edde746
2026-07-11 03:23:40 +02:00
parent 02a032d287
commit d86e328169
14 changed files with 405 additions and 154 deletions
+67 -12
View File
@@ -1071,8 +1071,8 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
/// Handle back button press
/// For non-host participants in Watch Together, shows leave session confirmation
Future<void> _handleBackButton() async {
if (_showPlayNextDialog || _showStillWatchingPrompt) {
Future<void> _handleBackButton({bool navigateHome = false}) async {
if (!navigateHome && (_showPlayNextDialog || _showStillWatchingPrompt)) {
_dismissPlaybackPromptForBack();
return;
}
@@ -1101,7 +1101,7 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
if (!mounted) return;
await _restoreSystemUiAndOrientation();
if (!mounted) return;
navigator.pop(true);
_finishPlayerNavigation(navigator, navigateHome: navigateHome);
}
}
}
@@ -1119,13 +1119,63 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
if (!mounted) return;
await _restoreSystemUiAndOrientation();
if (!mounted) return;
navigator.pop(true);
_finishPlayerNavigation(navigator, navigateHome: navigateHome);
}
} finally {
_isHandlingBack = false;
}
}
void _handleHomeButton() {
unawaited(_handleBackButton(navigateHome: true));
}
void _finishPlayerNavigation(NavigatorState navigator, {required bool navigateHome}) {
if (!navigateHome) {
navigator.pop(true);
return;
}
final onHome = _savedOnHome;
navigator.popUntil((route) => route.isFirst);
onHome?.call();
}
void _handleScreenPlayerNavigation(PlayerNavigationKey navigationKey) {
if (navigationKey == PlayerNavigationKey.home) {
_handleHomeButton();
return;
}
if (_showPlayNextDialog || _showStillWatchingPrompt) {
_dismissPlaybackPromptForBack();
return;
}
final disposition = resolvePlayerBackDisposition(
navigationKey: navigationKey,
contentStripVisible: _chromeController.contentStripVisible,
controlsVisible: _chromeController.controlsVisible,
);
switch (disposition) {
case PlayerBackDisposition.closeContentStrip:
_chromeController.setContentStripVisible(false);
return;
case PlayerBackDisposition.exitFullscreenIfActive:
unawaited(_handleScreenPhysicalEscape());
return;
case PlayerBackDisposition.hideControls:
_chromeController.hide();
return;
case PlayerBackDisposition.exitPlayer:
unawaited(_handleBackButton());
return;
}
}
Future<void> _handleScreenPhysicalEscape() async {
if (await FullscreenStateManager().exitFullscreenIfActive()) return;
if (mounted) _handleScreenPlayerNavigation(PlayerNavigationKey.back);
}
Future<void> _restoreSystemUiAndOrientation() async {
if (PlatformDetector.isDesktopOS() && _exitFullscreenOnPlayerClose) {
unawaited(FullscreenStateManager().exitFullscreen());
@@ -1433,15 +1483,20 @@ class VideoPlayerScreenState extends State<VideoPlayerScreen> with WidgetsBindin
canRequestFocus: isCurrentRoute,
onKeyEvent: (node, event) {
if (!isCurrentRoute) return KeyEventResult.ignored;
// On Windows/Linux with navigation off, consume ESC so Flutter's
// DismissAction doesn't trigger a route pop. The video controls'
// global key handler manages fullscreen/controls toggle instead.
if (!_videoPlayerNavigationEnabled && (Platform.isWindows || Platform.isLinux) && event.logicalKey.isBackKey) {
return KeyEventResult.handled;
final navigationKey = classifyPlayerNavigationKey(event, isAppleTV: PlatformDetector.isAppleTV());
if (navigationKey != PlayerNavigationKey.none) {
// Descendants (controls and sheets) own staged Back while focused.
// This fallback covers loading/error phases and focus drift.
if (!node.hasPrimaryFocus) return KeyEventResult.ignored;
if (navigationKey != PlayerNavigationKey.home && PlatformDetector.isTV() && event is KeyDownEvent) {
BackKeyCoordinator.markHandled();
}
return handlePlayerNavigationKeyAction(
event,
navigationKey,
() => _handleScreenPlayerNavigation(navigationKey),
);
}
// Back keys pass through — handled by PopScope (system back
// gesture) or overlay sheet's onKeyEvent.
if (event.logicalKey.isBackKey) return KeyEventResult.ignored;
// Hardware media play/pause must act even when focus rests on this
// node or a sibling overlay — otherwise the key only reveals the
// chrome and leaks to the (possibly stale/suspended) Android