fix(player): fit the performance overlay on short screens

Anchor the overlay to the screen and seekbar clearance, widen the card
on height-limited screens so sections pack side by side, and scale down
as a last resort instead of clipping the bottom sections.

close #1469
This commit is contained in:
edde746
2026-07-04 23:53:50 +02:00
parent 87aea49ab6
commit d7c0af9ad0
2 changed files with 43 additions and 13 deletions
+13 -4
View File
@@ -981,10 +981,19 @@ class _PlexVideoControlsState extends State<PlexVideoControls>
curve: Curves.easeInOut,
top: _showControls ? (isMobile ? 100.0 : 60.0) : 16.0,
left: 16,
child: AnimatedOpacity(
opacity: (!_autoHidePerformanceOverlay || _showControls) ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: IgnorePointer(child: PlayerPerformanceOverlay(player: widget.player)),
right: 16,
// Clear the bottom controls (same clearance as the skip button) so the
// overlay can shrink to fit instead of being clipped by the screen edge.
bottom: !_showControls ? 16.0 : (isMobile ? 80.0 : 115.0),
// Align loosens the tight constraints from the fully-anchored Positioned;
// without it the card would stretch to fill the whole region.
child: Align(
alignment: Alignment.topLeft,
child: AnimatedOpacity(
opacity: (!_autoHidePerformanceOverlay || _showControls) ? 1.0 : 0.0,
duration: const Duration(milliseconds: 200),
child: IgnorePointer(child: PlayerPerformanceOverlay(player: widget.player)),
),
),
),
if (_isScreenLocked)
@@ -1,3 +1,5 @@
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
@@ -111,15 +113,34 @@ class _PlayerPerformanceOverlayState extends State<PlayerPerformanceOverlay> {
]),
];
return Container(
constraints: const BoxConstraints(maxWidth: 400),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.8),
borderRadius: const BorderRadius.all(Radius.circular(8)),
boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.3), blurRadius: 4)],
),
child: Wrap(spacing: 24, runSpacing: 12, children: sections),
return LayoutBuilder(
builder: (context, constraints) {
// On height-limited screens (landscape phones) widen the card so the Wrap
// packs sections side by side instead of stacking into an off-screen column.
final compactHeight = constraints.hasBoundedHeight && constraints.maxHeight < 500;
final cardMaxWidth = compactHeight && constraints.hasBoundedWidth ? math.min(constraints.maxWidth, 560.0) : 400.0;
// Text scaling only inflates the intrinsic size that FittedBox scales right
// back down, trading layout sharpness for nothing on this diagnostics card.
return MediaQuery.withNoTextScaling(
// scaleDown is a no-op when the content fits; it only shrinks the card when
// it would otherwise be clipped by the bounds set at the embed site.
child: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.topLeft,
child: Container(
constraints: BoxConstraints(maxWidth: cardMaxWidth),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.8),
borderRadius: const BorderRadius.all(Radius.circular(8)),
boxShadow: [BoxShadow(color: Colors.black.withValues(alpha: 0.3), blurRadius: 4)],
),
child: Wrap(spacing: 24, runSpacing: 12, children: sections),
),
),
);
},
);
}