From 20953152b6fd9f6af1dc09ab12e32bfd68dbb7f5 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Sun, 3 May 2026 01:50:26 +0200 Subject: [PATCH] fix(tvos): dismiss overlays with back --- lib/main.dart | 3 + lib/screens/main_screen.dart | 4 + .../remote_session_dialog.dart | 199 +++++++++--------- 3 files changed, 109 insertions(+), 97 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c5981b48..f539562f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -836,6 +836,9 @@ class _MainAppState extends State with WidgetsBindingObserver { ...WidgetsApp.defaultShortcuts, const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(), const SingleActivator(LogicalKeyboardKey.gameButtonA): const ActivateIntent(), + const SingleActivator(LogicalKeyboardKey.goBack): const DismissIntent(), + const SingleActivator(LogicalKeyboardKey.browserBack): const DismissIntent(), + const SingleActivator(LogicalKeyboardKey.gameButtonB): const DismissIntent(), }, builder: (context, child) => ScaffoldMessenger( key: rootScaffoldMessengerKey, diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 6abce9e6..1330497a 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -893,6 +893,10 @@ class _MainScreenState extends State with RouteAware, WindowListener bool _suppressBackAfterPop = false; KeyEventResult _handleBackKey(KeyEvent event) { + if (ModalRoute.of(context)?.isCurrent != true) { + return KeyEventResult.ignored; + } + if (_suppressBackAfterPop && event.logicalKey.isBackKey) { if (event is KeyUpEvent) _suppressBackAfterPop = false; return KeyEventResult.handled; diff --git a/lib/widgets/companion_remote/remote_session_dialog.dart b/lib/widgets/companion_remote/remote_session_dialog.dart index 7242b095..b20e68d6 100644 --- a/lib/widgets/companion_remote/remote_session_dialog.dart +++ b/lib/widgets/companion_remote/remote_session_dialog.dart @@ -9,6 +9,7 @@ import '../../profiles/plex_home_service.dart'; import '../../profiles/profile_connection_registry.dart'; import '../../providers/companion_remote_provider.dart'; import '../../focus/focusable_button.dart'; +import '../../focus/key_event_utils.dart'; import '../../utils/app_logger.dart'; class RemoteSessionDialog extends StatefulWidget { @@ -99,109 +100,113 @@ class _RemoteSessionDialogState extends State { @override Widget build(BuildContext context) { - return Consumer( - builder: (context, provider, child) { - if (_isStarting) { - return Dialog( - child: Padding( - padding: const EdgeInsets.all(32.0), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const CircularProgressIndicator(), - const SizedBox(height: 16), - Text(t.companionRemote.session.startingServer, style: Theme.of(context).textTheme.titleMedium), - ], - ), - ), - ); - } - - if (_errorMessage != null) { - return AlertDialog( - title: Text(t.common.error), - content: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(t.companionRemote.session.failedToCreate), - const SizedBox(height: 8), - Text(_errorMessage!, style: const TextStyle(fontFamily: 'monospace')), - ], - ), - actions: [ - TextButton(onPressed: () => Navigator.of(context).pop(), child: Text(t.common.close)), - TextButton(onPressed: _ensureServerRunning, child: Text(t.common.retry)), - ], - ); - } - - return Dialog( - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 500), - child: SingleChildScrollView( - padding: const EdgeInsets.all(24.0), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Row( - children: [ - const Icon(Icons.phone_android, size: 32), - const SizedBox(width: 16), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(t.companionRemote.title, style: Theme.of(context).textTheme.headlineSmall), - const SizedBox(height: 4), - _buildStatusLine(context, provider), - ], - ), - ), - IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop()), - ], - ), - const SizedBox(height: 24), - - // Server status card - _buildServerStatus(context, provider), - - // Connected device info - if (provider.connectedDevice != null) ...[ + return Focus( + autofocus: true, + onKeyEvent: (node, event) => handleBackKeyNavigation(context, event), + child: Consumer( + builder: (context, provider, child) { + if (_isStarting) { + return Dialog( + child: Padding( + padding: const EdgeInsets.all(32.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const CircularProgressIndicator(), const SizedBox(height: 16), - _buildConnectedDevice(context, provider), + Text(t.companionRemote.session.startingServer, style: Theme.of(context).textTheme.titleMedium), ], + ), + ), + ); + } - const SizedBox(height: 24), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton.icon( - onPressed: _toggleServer, - icon: Icon(provider.isHostServerRunning ? Icons.stop : Icons.play_arrow), - label: Text( - provider.isHostServerRunning - ? t.companionRemote.session.stopServer - : t.companionRemote.session.startServer, - ), - ), - const SizedBox(width: 8), - FocusableButton( - onPressed: () => Navigator.of(context).pop(), - child: FilledButton( - onPressed: () => Navigator.of(context).pop(), - child: Text(t.companionRemote.session.minimize), - ), - ), - ], - ), + if (_errorMessage != null) { + return AlertDialog( + title: Text(t.common.error), + content: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(t.companionRemote.session.failedToCreate), + const SizedBox(height: 8), + Text(_errorMessage!, style: const TextStyle(fontFamily: 'monospace')), ], ), + actions: [ + TextButton(onPressed: () => Navigator.of(context).pop(), child: Text(t.common.close)), + TextButton(onPressed: _ensureServerRunning, child: Text(t.common.retry)), + ], + ); + } + + return Dialog( + child: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 500), + child: SingleChildScrollView( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + children: [ + const Icon(Icons.phone_android, size: 32), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(t.companionRemote.title, style: Theme.of(context).textTheme.headlineSmall), + const SizedBox(height: 4), + _buildStatusLine(context, provider), + ], + ), + ), + IconButton(icon: const Icon(Icons.close), onPressed: () => Navigator.of(context).pop()), + ], + ), + const SizedBox(height: 24), + + // Server status card + _buildServerStatus(context, provider), + + // Connected device info + if (provider.connectedDevice != null) ...[ + const SizedBox(height: 16), + _buildConnectedDevice(context, provider), + ], + + const SizedBox(height: 24), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton.icon( + onPressed: _toggleServer, + icon: Icon(provider.isHostServerRunning ? Icons.stop : Icons.play_arrow), + label: Text( + provider.isHostServerRunning + ? t.companionRemote.session.stopServer + : t.companionRemote.session.startServer, + ), + ), + const SizedBox(width: 8), + FocusableButton( + onPressed: () => Navigator.of(context).pop(), + child: FilledButton( + onPressed: () => Navigator.of(context).pop(), + child: Text(t.companionRemote.session.minimize), + ), + ), + ], + ), + ], + ), + ), ), - ), - ); - }, + ); + }, + ), ); }