feat(tokens): add design tokens, type scale, push scaffold, and root nav state

This commit is contained in:
2026-07-28 20:01:23 +02:00
parent 6dba89432e
commit 172b2a42ac
17 changed files with 856 additions and 30 deletions
+85
View File
@@ -0,0 +1,85 @@
import 'package:flutter/material.dart';
import '../tema/pluriwave_theme.dart';
import 'pluri_wave_scaffold.dart';
/// Design ADR-2: chrome is decided by route topology, not a parameter.
/// [PluriPushScaffold] is the ONE shape a second-level (pushed) screen may
/// take — root screens are plain body widgets living in `_PaginaPrincipal`
/// and never construct a Scaffold themselves.
///
/// The load-bearing part of this API is what it does NOT have: there is no
/// `bottomNavigationBar` parameter. That absence is what makes "second-level
/// screens have no tab bar" unrepresentable rather than a convention someone
/// can forget.
class PluriPushScaffold extends StatelessWidget {
const PluriPushScaffold({
super.key,
required this.title,
required this.body,
this.titleOverride,
this.leadingIcon = Icons.arrow_back_rounded,
this.onBack,
this.actions = const <Widget>[],
this.bottom,
this.floatingActionButton,
});
/// Styled once, here, with [PluriWaveTypography.screenTitle] — the reason
/// this is a String and not a Widget, so screens stop copy-pasting the
/// style. See [titleOverride] for the one documented exception.
final String title;
final Widget body;
/// Single documented exception to [title]: the full player's centered
/// "EN DIRECTO" pill (WU14). A second consumer means ADR-2 gets revisited,
/// not that this parameter quietly becomes the general case.
final Widget? titleOverride;
/// Defaults to a back arrow; `pantalla_reproductor.dart` dismisses with
/// `keyboard_arrow_down` instead — still one pushed route, one back
/// affordance, only the glyph differs.
final IconData leadingIcon;
/// Defaults to [Navigator.maybePop].
final VoidCallback? onBack;
final List<Widget> actions;
/// Optional persistent footer (e.g. a CTA).
final PreferredSizeWidget? bottom;
final Widget? floatingActionButton;
static const double headerHeight = 56;
/// Centralises route construction so the transition is uniform and a test
/// can assert "pushed, not index-switched". Precedent:
/// `PantallaReproductor.abrir`.
static Future<T?> push<T>(BuildContext context, WidgetBuilder builder) {
return Navigator.of(
context,
).push<T>(MaterialPageRoute<T>(builder: builder));
}
@override
Widget build(BuildContext context) {
final type = context.pluriType;
return PluriWaveScaffold(
appBar: AppBar(
toolbarHeight: headerHeight,
automaticallyImplyLeading: false,
leading: IconButton(
icon: Icon(leadingIcon),
onPressed: onBack ?? () => Navigator.maybePop(context),
),
title: titleOverride ?? Text(title, style: type.screenTitle),
actions: actions,
bottom: bottom,
),
body: body,
floatingActionButton: floatingActionButton,
);
}
}