diff --git a/lib/screens/media_detail_screen.dart b/lib/screens/media_detail_screen.dart index 8daa3372..55797380 100644 --- a/lib/screens/media_detail_screen.dart +++ b/lib/screens/media_detail_screen.dart @@ -1836,7 +1836,7 @@ class _MediaDetailScreenState extends State if (key.isLeftKey) { if (_focusedCastIndex > 0) { setState(() => _focusedCastIndex--); - scrollListToIndex(_castScrollController, _focusedCastIndex, itemExtent: _getResponsiveCardWidth() + 8 + 4, leadingPadding: 0); + scrollListToIndex(_castScrollController, _focusedCastIndex, itemExtent: _getResponsiveCardWidth() + 6 + 4, leadingPadding: 0); } return KeyEventResult.handled; } @@ -1845,7 +1845,7 @@ class _MediaDetailScreenState extends State if (key.isRightKey) { if (_focusedCastIndex < roleCount - 1) { setState(() => _focusedCastIndex++); - scrollListToIndex(_castScrollController, _focusedCastIndex, itemExtent: _getResponsiveCardWidth() + 8 + 4, leadingPadding: 0); + scrollListToIndex(_castScrollController, _focusedCastIndex, itemExtent: _getResponsiveCardWidth() + 6 + 4, leadingPadding: 0); } return KeyEventResult.handled; } @@ -2651,6 +2651,7 @@ class _MediaDetailScreenState extends State key: _relatedHubKeys[i], hub: _relatedHubs[i], icon: _getRelatedHubIcon(_relatedHubs[i]), + inset: true, onVerticalNavigation: (isUp) => _handleRelatedHubNavigation(i, isUp), ), const SizedBox(height: 8), @@ -2764,7 +2765,7 @@ class _MediaDetailScreenState extends State /// Uses same layout pattern as seasons/extras (ListView.builder + Padding(horizontal: 2)) Widget _buildCastSection(PlexMetadata metadata) { final cardWidth = _getResponsiveCardWidth(); - const innerPadding = 4.0; + const innerPadding = 3.0; final imageSize = cardWidth; // image + inner padding + text area + outer list padding + focus scale headroom final containerHeight = imageSize + innerPadding * 2 + 66 + 16; @@ -2782,6 +2783,7 @@ class _MediaDetailScreenState extends State builder: (scrollController) => ListView.builder( controller: scrollController, scrollDirection: Axis.horizontal, + clipBehavior: Clip.none, padding: const EdgeInsets.symmetric(vertical: 5), itemCount: metadata.role!.length, itemBuilder: (context, index) { @@ -2870,6 +2872,7 @@ class _MediaDetailScreenState extends State builder: (scrollController) => ListView.builder( controller: scrollController, scrollDirection: Axis.horizontal, + clipBehavior: Clip.none, padding: const EdgeInsets.symmetric(vertical: 5), itemCount: _extras!.length, itemBuilder: (context, index) { diff --git a/lib/widgets/hub_section.dart b/lib/widgets/hub_section.dart index 6613021b..15ed0875 100644 --- a/lib/widgets/hub_section.dart +++ b/lib/widgets/hub_section.dart @@ -54,6 +54,10 @@ class HubSection extends StatefulWidget { /// Used to navigate focus to the sidebar. final VoidCallback? onNavigateToSidebar; + /// When true, removes internal horizontal padding (header + list). + /// Use when the parent already provides edge spacing (e.g. inside Padding(16)). + final bool inset; + const HubSection({ super.key, required this.hub, @@ -66,6 +70,7 @@ class HubSection extends StatefulWidget { this.onBack, this.onNavigateUp, this.onNavigateToSidebar, + this.inset = false, }); @override @@ -83,7 +88,7 @@ class HubSectionState extends State { /// Item extent for scroll calculations double _itemExtent = 0; - static const double _leadingPadding = 12.0; + double get _leadingPadding => widget.inset ? 0.0 : 12.0; Timer? _longPressTimer; bool _isSelectKeyDown = false; @@ -326,13 +331,13 @@ class HubSectionState extends State { children: [ // Hub header (NOT focusable - titles should not be focusable) Padding( - padding: const EdgeInsets.fromLTRB(8, 2, 8, 2), + padding: widget.inset ? const EdgeInsets.symmetric(vertical: 2) : const EdgeInsets.fromLTRB(8, 2, 8, 2), child: ExcludeFocus( child: InkWell( onTap: widget.hub.more ? () => _navigateToHubDetail(context) : null, borderRadius: BorderRadius.circular(tokens(context).radiusSm), child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 2), + padding: widget.inset ? const EdgeInsets.symmetric(vertical: 2) : const EdgeInsets.symmetric(horizontal: 4, vertical: 2), child: Row( mainAxisSize: MainAxisSize.min, children: [ @@ -422,7 +427,7 @@ class HubSectionState extends State { controller: scrollController, scrollDirection: Axis.horizontal, clipBehavior: Clip.none, - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + padding: widget.inset ? const EdgeInsets.symmetric(vertical: 2) : const EdgeInsets.symmetric(horizontal: 8, vertical: 2), itemCount: isKeyboardMode ? _totalItemCount : widget.hub.items.length, itemBuilder: (context, index) { final isItemFocused = hasFocus && index == _focusedIndex; @@ -430,7 +435,7 @@ class HubSectionState extends State { // "View All" card at end if (index == widget.hub.items.length) { return Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), + padding: widget.inset ? const EdgeInsets.only(right: 4) : const EdgeInsets.symmetric(horizontal: 2), child: FocusBuilders.buildLockedFocusWrapper( context: context, isFocused: isItemFocused, @@ -468,7 +473,7 @@ class HubSectionState extends State { final item = widget.hub.items[index]; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 2), + padding: widget.inset ? const EdgeInsets.only(right: 4) : const EdgeInsets.symmetric(horizontal: 2), child: FocusBuilders.buildLockedFocusWrapper( context: context, isFocused: isItemFocused, @@ -496,7 +501,7 @@ class HubSectionState extends State { ) else Padding( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + padding: widget.inset ? const EdgeInsets.symmetric(vertical: 8) : const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Text( t.messages.noItemsAvailable, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey),