Files
plezy/lib/utils/grid_size_calculator.dart
T

89 lines
3.7 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 15 (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 max cross-axis extent accounting for outer padding.
/// [density] is an int 15.
static double getMaxCrossAxisExtentWithPadding(BuildContext context, int density, double horizontalPadding) {
final screenWidth = MediaQuery.sizeOf(context).width;
final availableWidth = screenWidth - horizontalPadding;
final f = LibraryDensity.factor(density);
// TV-specific sizing for 10ft viewing distance
if (PlatformDetector.isTV()) {
final divisor = _lerp(12, 6, f);
final maxItemWidth = _lerp(140, 240, f);
return (availableWidth / divisor).clamp(0, maxItemWidth);
}
if (ScreenBreakpoints.isWideTabletOrLarger(screenWidth)) {
final divisor = _lerp(10, 5, f);
final maxItemWidth = _lerp(140, 280, f);
return (availableWidth / divisor).clamp(0, maxItemWidth);
} else if (ScreenBreakpoints.isTablet(screenWidth)) {
final targetItemCount = _lerp(6, 3, f);
return availableWidth / targetItemCount;
} else {
final targetItemCount = _lerp(5, 2, f);
return availableWidth / targetItemCount;
}
}
/// Calculates the number of columns for a given available width.
///
/// Uses the same formula as Flutter's SliverGridDelegateWithMaxCrossAxisExtent:
/// `((crossAxisExtent + crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil()`
///
/// [crossAxisExtent] should come from layout constraints (e.g. `SliverLayoutBuilder`
/// or `LayoutBuilder`), not from `MediaQuery`, to account for sidebars or other
/// elements that reduce the grid's actual width.
static int getColumnCount(
double crossAxisExtent,
double maxCrossAxisExtent, {
double crossAxisSpacing = GridLayoutConstants.crossAxisSpacing,
}) {
return ((crossAxisExtent + crossAxisSpacing) / (maxCrossAxisExtent + crossAxisSpacing)).ceil().clamp(1, 100);
}
static double getCellWidthForColumnCount(
double crossAxisExtent,
int columnCount, {
double crossAxisSpacing = GridLayoutConstants.crossAxisSpacing,
}) {
return (crossAxisExtent - (crossAxisSpacing * (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;
}
}