From 3d3c8641ed02c604817d21dd02ab3dad69e75a50 Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Thu, 5 Feb 2026 10:54:40 +0100 Subject: [PATCH] fix: suspend hero animation when not visible --- lib/mixins/tab_visibility_aware.dart | 8 ++++++++ lib/screens/discover_screen.dart | 19 ++++++++++++++++--- lib/screens/main_screen.dart | 25 +++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 lib/mixins/tab_visibility_aware.dart diff --git a/lib/mixins/tab_visibility_aware.dart b/lib/mixins/tab_visibility_aware.dart new file mode 100644 index 00000000..28a4308b --- /dev/null +++ b/lib/mixins/tab_visibility_aware.dart @@ -0,0 +1,8 @@ +/// Mixin for screens that need to react to tab visibility changes. +/// +/// Used by MainScreen to pause expensive work (e.g. animation tickers) +/// when the screen's tab is no longer visible, and resume it when shown again. +mixin TabVisibilityAware { + void onTabShown(); + void onTabHidden(); +} diff --git a/lib/screens/discover_screen.dart b/lib/screens/discover_screen.dart index 1ced379f..539e7010 100644 --- a/lib/screens/discover_screen.dart +++ b/lib/screens/discover_screen.dart @@ -22,6 +22,7 @@ import 'profile/profile_switch_screen.dart'; import '../providers/user_profile_provider.dart'; import '../providers/settings_provider.dart'; import '../mixins/refreshable.dart'; +import '../mixins/tab_visibility_aware.dart'; import '../i18n/strings.g.dart'; import '../mixins/item_updatable.dart'; import '../mixins/watch_state_aware.dart'; @@ -53,6 +54,7 @@ class _DiscoverScreenState extends State FullRefreshable, ItemUpdatable, WatchStateAware, + TabVisibilityAware, SingleTickerProviderStateMixin, WidgetsBindingObserver { static const Duration _heroAutoScrollDuration = Duration(seconds: 8); @@ -470,6 +472,19 @@ class _DiscoverScreenState extends State _startAutoScroll(); } + @override + void onTabHidden() { + _autoScrollTimer?.cancel(); + _indicatorAnimationController.stop(); + } + + @override + void onTabShown() { + if (!_isAutoScrollPaused) { + _startAutoScroll(); + } + } + // Helper method to calculate visible dot range (max 5 dots) ({int start, int end}) _getVisibleDotRange() { final totalDots = _onDeck.length; @@ -1277,9 +1292,7 @@ class _DiscoverScreenState extends State final maxWidth = dotSize * 3; // 24px for normal, 15px for small final fillWidth = dotSize + ((maxWidth - dotSize) * _indicatorAnimationController.value); final onSurface = Theme.of(context).colorScheme.onSurface; - return AnimatedContainer( - duration: tokens(context).slow, - curve: Curves.easeInOut, + return Container( margin: const EdgeInsets.symmetric(horizontal: 4), width: maxWidth, height: dotSize, diff --git a/lib/screens/main_screen.dart b/lib/screens/main_screen.dart index 94a1fd9e..e7010cb8 100644 --- a/lib/screens/main_screen.dart +++ b/lib/screens/main_screen.dart @@ -14,6 +14,7 @@ import '../utils/platform_detector.dart'; import '../utils/video_player_navigation.dart'; import '../main.dart'; import '../mixins/refreshable.dart'; +import '../mixins/tab_visibility_aware.dart'; import '../navigation/navigation_tabs.dart'; import '../providers/multi_server_provider.dart'; import '../providers/server_state_provider.dart'; @@ -495,10 +496,23 @@ class _MainScreenState extends State with RouteAware, WindowListener } } + @override + void didPushNext() { + // Called when a child route is pushed on top (e.g., video player) + if (_currentIndex == 0 && !_isOffline) { + if (_discoverKey.currentState case final TabVisibilityAware aware) { + aware.onTabHidden(); + } + } + } + @override void didPopNext() { // Called when returning to this route from a child route (e.g., from video player) if (_currentIndex == 0 && !_isOffline) { + if (_discoverKey.currentState case final TabVisibilityAware aware) { + aware.onTabShown(); + } _onDiscoverBecameVisible(); } } @@ -593,8 +607,19 @@ class _MainScreenState extends State with RouteAware, WindowListener // Skip online-only screen logic in offline mode if (!_isOffline) { + // Pause/resume discover auto-scroll when switching tabs + if (previousIndex == 0 && index != 0) { + if (_discoverKey.currentState case final TabVisibilityAware aware) { + aware.onTabHidden(); + } + } // Notify discover screen when it becomes visible via tab switch if (index == 0) { + if (previousIndex != 0) { + if (_discoverKey.currentState case final TabVisibilityAware aware) { + aware.onTabShown(); + } + } _onDiscoverBecameVisible(); } // Ensure the libraries screen applies focus when brought into view