Files
pluriwave/lib/widgets/pluri_layout.dart
FreeTLab 7ebe0b77a4 fix(layout): introduce the prototype's 3-tier horizontal padding scale
The prototype runs three horizontal padding tiers (t4): 20px for
section titles/eyebrows (lines 153, 254, 299, 511), 16px for cards
(lines 327, 512, 610), 12px for background-less list rows (lines 174,
226, 301). The build had collapsed all three into a single
PluriLayout.horizontal = 16, used everywhere regardless of context.

Add PluriLayout.titleHorizontal (20) and PluriLayout.rowHorizontal
(12) alongside the existing `horizontal` (16, unchanged -- it already
covers the card tier). horizontal keeps every one of its ~30 existing
call sites unchanged.

Committed ahead of S2 (item 6 in this pass) because that item's
PluriRootHeader edit reuses titleHorizontal/rowHorizontal for the
header's own padding, matching the prototype's own header spec exactly
(e.g. Alarmas padding:0 12px 0 20px) -- a real dependency, not just
numbering.

S5, Tier 1 visual-fidelity pass (audit id 2521).
2026-07-29 21:28:48 +02:00

76 lines
2.7 KiB
Dart

import 'package:flutter/material.dart';
import '../tema/pluriwave_theme.dart';
import 'mini_reproductor.dart';
import 'pluri_bottom_navigation.dart';
abstract final class PluriLayout {
// S5 (Tier 1 visual fidelity): the prototype (`t4`) runs a 3-tier
// horizontal padding scale — 20px for section titles/eyebrows (lines
// 153, 254, 299, 511), 16px for cards (lines 327, 512, 610), 12px for
// background-less list rows (lines 174, 226, 301). [horizontal] IS the
// card tier — its value and every existing call site stay unchanged;
// [titleHorizontal] and [rowHorizontal] are new.
static const double titleHorizontal = 20;
static const double horizontal = 16;
static const double rowHorizontal = 12;
static const double sectionGap = 12;
static const double panelGap = 12;
static const double compactGap = 8;
/// `app.dart`'s `bottomNavigationBar` stacks `MiniReproductor` above
/// `PluriBottomNavigation` with no gap between them, wrapped in a
/// `SafeArea(minimum: EdgeInsets.only(bottom: compactGap))` — this is that
/// same sum, derived from the two chrome widgets' own measured heights
/// instead of a guessed constant. See
/// `pluri_bottom_navigation_test.dart`'s composed-chrome-height assertion.
static const double bottomChromeInset =
MiniReproductor.altura + PluriBottomNavigation.altura + compactGap;
/// ADR-7(b): `bottomChromeInset` assumes `MiniReproductor` is visible.
/// Escuchar hides it (design's one exception — the embedded hero already
/// shows the same station), so its content needs less bottom padding.
static const double escucharBottomChromeInset =
bottomChromeInset - MiniReproductor.altura;
static const EdgeInsets pageListPadding = EdgeInsets.fromLTRB(
0,
0,
0,
bottomChromeInset,
);
static const EdgeInsets pageContentPadding = EdgeInsets.symmetric(
horizontal: horizontal,
);
/// S5: the title/eyebrow tier's own content padding — e.g. a section
/// heading living directly on the page background, outside any card.
static const EdgeInsets titleContentPadding = EdgeInsets.symmetric(
horizontal: titleHorizontal,
);
static const EdgeInsets sheetPadding = EdgeInsets.all(18);
}
class PluriPanelColumn extends StatelessWidget {
const PluriPanelColumn({super.key, required this.children, this.gap});
final List<Widget> children;
final double? gap;
@override
Widget build(BuildContext context) {
final resolvedGap = gap ?? context.pluriTokens.spacingSm + 4;
return Column(
children: [
for (var i = 0; i < children.length; i++) ...[
if (i > 0) SizedBox(height: resolvedGap),
children[i],
],
],
);
}
}