fix(libraries): give square music grids an inter-card gutter
Square-card (music) grids had zero delegate spacing - the only separation was each card's internal 3px padding, and square shapes are excluded from the full-card spacing path, so playlist and album tiles sat nearly edge to edge. Square grids now get an 8px cross/main gutter (automotive keeps its larger spacing); poster, list, and full-card layouts are unchanged.
This commit is contained in:
@@ -51,6 +51,11 @@ class GridLayoutConstants {
|
||||
/// reserves ([posterAspectRatio] adds 0.3 to the 2:3 image's denominator).
|
||||
static const double squareGridCellAspectRatio = 2 / 2.3;
|
||||
|
||||
/// Inter-card gutter for square (music) grids on touch platforms. Cards
|
||||
/// already carry their own 3px internal padding, so the net visual gutter
|
||||
/// is ~14px horizontal. Automotive keeps its larger [crossAxisSpacing].
|
||||
static const double squareGridSpacing = 8.0;
|
||||
|
||||
static double get crossAxisSpacing => PlatformDetector.isAutomotive() ? 24 : 0;
|
||||
static double get mainAxisSpacing => PlatformDetector.isAutomotive() ? 24 : 0;
|
||||
|
||||
|
||||
@@ -36,9 +36,22 @@ class MediaGridDelegate {
|
||||
return maxCrossAxisExtent;
|
||||
}
|
||||
|
||||
static double spacingFor({required BuildContext context, bool fullBleedImage = false}) {
|
||||
/// Inter-cell gutter for the resolved shape. Square (music) grids get
|
||||
/// [GridLayoutConstants.squareGridSpacing] so cards have breathing room;
|
||||
/// every other shape keeps the platform default (0, or 24 on automotive).
|
||||
/// Full-bleed TV grids use the scaled full-card gutter.
|
||||
static double spacingFor({
|
||||
required BuildContext context,
|
||||
bool useWideAspectRatio = false,
|
||||
bool fullBleedImage = false,
|
||||
CardShape? shape,
|
||||
}) {
|
||||
if (PlatformDetector.isAutomotive()) return GridLayoutConstants.crossAxisSpacing;
|
||||
if (!fullBleedImage) return GridLayoutConstants.crossAxisSpacing;
|
||||
if (!fullBleedImage) {
|
||||
return _resolveShape(shape, useWideAspectRatio) == CardShape.square
|
||||
? GridLayoutConstants.squareGridSpacing
|
||||
: GridLayoutConstants.crossAxisSpacing;
|
||||
}
|
||||
return GridLayoutConstants.fullCardGridSpacingForScale(TvLayoutConstants.scaleOf(context));
|
||||
}
|
||||
|
||||
@@ -101,7 +114,12 @@ class MediaGridGeometry {
|
||||
bool fullBleedImage = false,
|
||||
CardShape? shape,
|
||||
}) {
|
||||
final spacing = MediaGridDelegate.spacingFor(context: context, fullBleedImage: fullBleedImage);
|
||||
final spacing = MediaGridDelegate.spacingFor(
|
||||
context: context,
|
||||
useWideAspectRatio: useWideAspectRatio,
|
||||
fullBleedImage: fullBleedImage,
|
||||
shape: shape,
|
||||
);
|
||||
final aspectRatio = MediaGridDelegate.aspectRatioFor(
|
||||
useWideAspectRatio: useWideAspectRatio,
|
||||
fullBleedImage: fullBleedImage,
|
||||
|
||||
@@ -20,6 +20,7 @@ import 'package:plezy/services/multi_server_manager.dart';
|
||||
import 'package:plezy/services/plex_client.dart';
|
||||
import 'package:plezy/services/plex_api_cache.dart';
|
||||
import 'package:plezy/services/settings_service.dart';
|
||||
import 'package:plezy/utils/layout_constants.dart';
|
||||
import 'package:plezy/utils/platform_detector.dart';
|
||||
import 'package:plezy/widgets/card_inflation_budget.dart';
|
||||
import 'package:plezy/widgets/focusable_media_card.dart';
|
||||
@@ -182,6 +183,13 @@ void main() {
|
||||
.every((card) => card.cardShapeOverride == CardShape.square),
|
||||
isTrue,
|
||||
);
|
||||
|
||||
// Square grids keep their square gutter spacing even on TV full-card
|
||||
// layout (which is disabled for square shapes).
|
||||
final gridDelegate =
|
||||
tester.widget<SliverGrid>(find.byType(SliverGrid)).gridDelegate as SliverGridDelegateWithMaxCrossAxisExtent;
|
||||
expect(gridDelegate.crossAxisSpacing, GridLayoutConstants.squareGridSpacing);
|
||||
expect(gridDelegate.mainAxisSpacing, GridLayoutConstants.squareGridSpacing);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,64 @@ void main() {
|
||||
expect(MediaGridDelegate.aspectRatioFor(useWideAspectRatio: true), GridLayoutConstants.episodeGridCellAspectRatio);
|
||||
});
|
||||
|
||||
testWidgets('square grid delegates use square gutters, others unchanged', (tester) async {
|
||||
tester.view.devicePixelRatio = 1.0;
|
||||
tester.view.physicalSize = const Size(1280, 720);
|
||||
addTearDown(() {
|
||||
tester.view.resetDevicePixelRatio();
|
||||
tester.view.resetPhysicalSize();
|
||||
});
|
||||
|
||||
SliverGridDelegateWithMaxCrossAxisExtent? square;
|
||||
SliverGridDelegateWithMaxCrossAxisExtent? poster;
|
||||
SliverGridDelegateWithMaxCrossAxisExtent? wide;
|
||||
SliverGridDelegateWithMaxCrossAxisExtent? fullBleed;
|
||||
await tester.pumpWidget(
|
||||
_TestApp(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
square = MediaGridGeometry.resolve(
|
||||
context: context,
|
||||
crossAxisExtent: 1280,
|
||||
density: LibraryDensity.defaultValue,
|
||||
shape: CardShape.square,
|
||||
).delegate;
|
||||
poster = MediaGridGeometry.resolve(
|
||||
context: context,
|
||||
crossAxisExtent: 1280,
|
||||
density: LibraryDensity.defaultValue,
|
||||
).delegate;
|
||||
wide = MediaGridGeometry.resolve(
|
||||
context: context,
|
||||
crossAxisExtent: 1280,
|
||||
density: LibraryDensity.defaultValue,
|
||||
useWideAspectRatio: true,
|
||||
).delegate;
|
||||
fullBleed = MediaGridGeometry.resolve(
|
||||
context: context,
|
||||
crossAxisExtent: 1280,
|
||||
density: LibraryDensity.defaultValue,
|
||||
fullBleedImage: true,
|
||||
).delegate;
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// Square (music) grids get breathing room.
|
||||
expect(square!.crossAxisSpacing, GridLayoutConstants.squareGridSpacing);
|
||||
expect(square!.mainAxisSpacing, GridLayoutConstants.squareGridSpacing);
|
||||
// Poster and wide grids keep the platform default (0 off-automotive).
|
||||
expect(poster!.crossAxisSpacing, GridLayoutConstants.crossAxisSpacing);
|
||||
expect(poster!.mainAxisSpacing, GridLayoutConstants.crossAxisSpacing);
|
||||
expect(wide!.crossAxisSpacing, GridLayoutConstants.crossAxisSpacing);
|
||||
expect(wide!.mainAxisSpacing, GridLayoutConstants.crossAxisSpacing);
|
||||
// Full-bleed TV gutters are unchanged.
|
||||
expect(fullBleed!.crossAxisSpacing, GridLayoutConstants.fullCardGridSpacingForScale(0.85));
|
||||
expect(fullBleed!.mainAxisSpacing, fullBleed!.crossAxisSpacing);
|
||||
});
|
||||
|
||||
test('list layout sizes square cards 1:1', () {
|
||||
final base = MediaCardListLayout.basePosterWidth(LibraryDensity.defaultValue);
|
||||
expect(MediaCardListLayout.posterWidth(density: LibraryDensity.defaultValue, shape: CardShape.square), base);
|
||||
|
||||
Reference in New Issue
Block a user