Files
plezy/lib/utils/grid_size_calculator.dart
T
edde746 fcb167e661 fix(library): size hub detail posters like home rows and library grids
Home hub rows showed ~3 large posters per row while the hub's "see all"
page packed 5 small ones on the same 360dp phone. Hub detail was the
only surface on the padding-aware target-count formula, which resolves
ceil(lerp(5, 2, f)) columns regardless of screen width (and 9+ columns
on TV). Drop the flag so hub detail shares the fixed-extent formula
with every other grid, and delete the now-unused
getMaxCrossAxisExtentWithPadding and its usePaddingAware plumbing.

close #2039
2026-08-20 14:34:27 +02:00

62 lines
3.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import '../services/settings_service.dart' show LibraryDensity;
import 'layout_constants.dart';
import 'platform_detector.dart';
class GridSizeCalculator {
static double _lerp(double min, double max, double t) => min + (max - min) * t;
/// Calculates the maximum cross-axis extent for grid items based on screen size and density.
/// [density] is an int 1–5 (1 = most compact, 5 = most comfortable).
static double getMaxCrossAxisExtent(BuildContext context, int density) {
final screenWidth = MediaQuery.sizeOf(context).width;
final f = LibraryDensity.factor(density);
if (PlatformDetector.isTV()) return _lerp(120, 220, f);
if (ScreenBreakpoints.isDesktopOrLarger(screenWidth)) return _lerp(140, 280, f);
if (ScreenBreakpoints.isTablet(screenWidth)) return _lerp(120, 230, f);
return _lerp(100, 200, f);
}
/// Calculates the number of columns for a given available width.
///
/// Matches Flutter's SliverGridDelegateWithMaxCrossAxisExtent exactly (see
/// rendering/sliver_grid.dart), so this navigation column count equals the
/// number of columns the grid actually renders. A mismatch makes dpad "down"
/// (`index + columnCount`) land diagonally — see issue #1288. Note the spacing
/// is added to the denominator only, not the numerator:
/// `(crossAxisExtent / (maxCrossAxisExtent + crossAxisSpacing)).ceil()`
///
/// [crossAxisExtent] should come from layout constraints (e.g.
/// `SliverCrossAxisLayoutBuilder` or `LayoutBuilder`), not from `MediaQuery`,
/// to account for sidebars or other elements that reduce the grid's actual
/// width. Never use a plain `SliverLayoutBuilder` for this: its constraints
/// include the scroll offset, so it rebuilds the whole grid every scroll tick.
static int getColumnCount(double crossAxisExtent, double maxCrossAxisExtent, {double? crossAxisSpacing}) {
final effectiveSpacing = crossAxisSpacing ?? GridLayoutConstants.crossAxisSpacing;
return (crossAxisExtent / (maxCrossAxisExtent + effectiveSpacing)).ceil().clamp(1, 100);
}
static double getCellWidthForColumnCount(double crossAxisExtent, int columnCount, {double? crossAxisSpacing}) {
final effectiveSpacing = crossAxisSpacing ?? GridLayoutConstants.crossAxisSpacing;
return (crossAxisExtent - (effectiveSpacing * (columnCount - 1))) / columnCount;
}
/// Computes the actual cell width that a grid with [getMaxCrossAxisExtent] would produce
/// for the given [availableWidth]. This matches SliverGridDelegateWithMaxCrossAxisExtent's
/// internal calculation, so horizontal scroll lists can use the same width as grids.
static double getCellWidth(double availableWidth, BuildContext context, int density) {
final maxExtent = getMaxCrossAxisExtent(context, density);
final columns = getColumnCount(availableWidth, maxExtent);
return getCellWidthForColumnCount(availableWidth, columns);
}
static bool isFirstRow(int index, int columnCount) {
return index < columnCount;
}
static bool isFirstColumn(int index, int columnCount) {
return index % columnCount == 0;
}
}