From c354193187bbb00a7692761750b849f83b7e0a2f Mon Sep 17 00:00:00 2001 From: edde746 <86283021+edde746@users.noreply.github.com> Date: Fri, 22 May 2026 04:30:22 +0200 Subject: [PATCH] fix(tv): fade media detail transitions --- lib/screens/media_detail_screen.dart | 18 ++++++++++++++++++ lib/utils/media_navigation_helper.dart | 12 ++---------- lib/widgets/media_card.dart | 16 +++------------- lib/widgets/media_context_menu.dart | 8 ++++---- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index 1d9f5f72..d8019f30 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -101,6 +101,24 @@ class MediaDetailScreen extends StatefulWidget { State createState() => _MediaDetailScreenState(); } +PageRoute mediaDetailRoute({required MediaItem metadata, bool isOffline = false, int? initialSeasonIndex}) { + final page = MediaDetailScreen(metadata: metadata, isOffline: isOffline, initialSeasonIndex: initialSeasonIndex); + if (!PlatformDetector.isTV()) return MaterialPageRoute(builder: (_) => page); + + return PageRouteBuilder( + opaque: false, + pageBuilder: (_, __, ___) => page, + transitionsBuilder: (_, animation, __, child) { + return FadeTransition( + opacity: CurvedAnimation(parent: animation, curve: Curves.easeOutCubic, reverseCurve: Curves.easeInCubic), + child: child, + ); + }, + transitionDuration: AppDurations.animMedium, + reverseTransitionDuration: AppDurations.animMedium, + ); +} + class _MediaDetailScreenState extends State with WatchStateAware, DeletionAware, MountedSetStateMixin, ServerBoundMediaMixin, RouteAware { /// Public input alias — used as the live source of truth until the detail diff --git a/lib/utils/media_navigation_helper.dart b/lib/utils/media_navigation_helper.dart index c12426ea..b13e7c5a 100644 --- a/lib/utils/media_navigation_helper.dart +++ b/lib/utils/media_navigation_helper.dart @@ -131,10 +131,7 @@ Future navigateToMediaItem( ); final result = await Navigator.push( context, - MaterialPageRoute( - builder: (context) => - MediaDetailScreen(metadata: showStub, isOffline: isOffline, initialSeasonIndex: mi.index), - ), + mediaDetailRoute(metadata: showStub, isOffline: isOffline, initialSeasonIndex: mi.index), ); if (result == true) { onRefresh?.call(mi.id); @@ -154,12 +151,7 @@ Future _showDetail( bool isOffline, void Function(String)? onRefresh, ) async { - final result = await Navigator.push( - context, - MaterialPageRoute( - builder: (context) => MediaDetailScreen(metadata: mi, isOffline: isOffline), - ), - ); + final result = await Navigator.push(context, mediaDetailRoute(metadata: mi, isOffline: isOffline)); if (result == true) { onRefresh?.call(mi.id); } diff --git a/lib/widgets/media_card.dart b/lib/widgets/media_card.dart index 4db94f24..f133cdf4 100644 --- a/lib/widgets/media_card.dart +++ b/lib/widgets/media_card.dart @@ -949,10 +949,7 @@ void _navigateToSeason(BuildContext context, MediaItem episode, {bool isOffline ); Navigator.push( context, - MaterialPageRoute( - builder: (_) => - MediaDetailScreen(metadata: showStub, isOffline: isOffline, initialSeasonIndex: episode.parentIndex), - ), + mediaDetailRoute(metadata: showStub, isOffline: isOffline, initialSeasonIndex: episode.parentIndex), ); } else if (episode.parentId != null) { // Fallback: navigate to season directly if no grandparent @@ -969,12 +966,7 @@ void _navigateToSeason(BuildContext context, MediaItem episode, {bool isOffline serverId: episode.serverId, serverName: episode.serverName, ); - Navigator.push( - context, - MaterialPageRoute( - builder: (_) => MediaDetailScreen(metadata: seasonStub, isOffline: isOffline), - ), - ); + Navigator.push(context, mediaDetailRoute(metadata: seasonStub, isOffline: isOffline)); } } @@ -1016,9 +1008,7 @@ void _navigateToDetail(BuildContext context, MediaItem mi, {bool isOffline = fal Navigator.push( context, - MaterialPageRoute( - builder: (_) => MediaDetailScreen(metadata: target, isOffline: isOffline, initialSeasonIndex: initialSeasonIndex), - ), + mediaDetailRoute(metadata: target, isOffline: isOffline, initialSeasonIndex: initialSeasonIndex), ); } diff --git a/lib/widgets/media_context_menu.dart b/lib/widgets/media_context_menu.dart index e41bc327..2157bb86 100644 --- a/lib/widgets/media_context_menu.dart +++ b/lib/widgets/media_context_menu.dart @@ -654,7 +654,7 @@ class MediaContextMenuState extends State { await _navigateToRelated( context, mediaItem!.kind == MediaKind.season ? mediaItem.parentId : mediaItem.grandparentId, - (item) => MediaDetailScreen(metadata: item), + (item) => mediaDetailRoute(metadata: item), t.messages.errorLoadingSeries, ); break; @@ -667,7 +667,7 @@ class MediaContextMenuState extends State { await _navigateToRelated( context, seasonParentKey, - (show) => MediaDetailScreen(metadata: show, initialSeasonIndex: seasonIndex), + (show) => mediaDetailRoute(metadata: show, initialSeasonIndex: seasonIndex), t.messages.errorLoadingSeason, ); break; @@ -800,7 +800,7 @@ class MediaContextMenuState extends State { Future _navigateToRelated( BuildContext context, String? id, - Widget Function(MediaItem) screenBuilder, + Route Function(MediaItem) routeBuilder, String errorPrefix, ) async { if (id == null) return; @@ -810,7 +810,7 @@ class MediaContextMenuState extends State { try { final metadata = await client.fetchItem(id); if (metadata != null && context.mounted) { - await Navigator.push(context, MaterialPageRoute(builder: (context) => screenBuilder(metadata))); + await Navigator.push(context, routeBuilder(metadata)); widget.onRefresh?.call(_itemId()); } } catch (e) {