feat(tutorial): add 9-screen help/tutorial carousel
Add PantallaTutorialAyuda, a PageView-based carousel covering saved
stations/groups, per-station equalizer, recording, adaptive alarms,
Android Auto favorites, auto-reconnect, snooze duration, custom
stations, and a closing summary with a "watch it again" reminder.
ServicioTutorialAyuda persists a one-time seen flag so the carousel
shows once via mostrarSiProcede, independent of entry point; the
final page's CTA label depends on the primerArranque constructor
parameter ("Empezar a escuchar" vs "Cerrar").
Translate the new copy into all 13 supported locales and update
helpSubtitle to describe the new entry point.
This commit is contained in:
@@ -29,41 +29,59 @@ class PluriRootHeader extends StatelessWidget {
|
||||
/// needs nothing extra here).
|
||||
final List<Widget> actions;
|
||||
|
||||
/// Fix `safearea-top-inset`: this is the CONTENT row's height only —
|
||||
/// NOT this widget's total rendered height. `app.dart`'s root
|
||||
/// `SafeArea(top: false, ...)` deliberately excludes the top inset (so
|
||||
/// each root's own full-bleed background paints genuinely edge-to-edge
|
||||
/// behind the status bar), which left this header's title/actions row
|
||||
/// with zero top-inset awareness — flush at y=0 under the status bar /
|
||||
/// camera cutout on every device. This widget now adds
|
||||
/// `MediaQuery.paddingOf(context).top` ABOVE this content height itself
|
||||
/// (see [build]), so the total rendered height is
|
||||
/// `height + MediaQuery.paddingOf(context).top`. Callers doing
|
||||
/// total-height math (none currently do — checked every `PluriRootHeader`
|
||||
/// call site) must add that inset separately; this constant's MEANING
|
||||
/// (content height) is unchanged.
|
||||
static const double height = 56;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final type = context.pluriType;
|
||||
final l10n = AppLocalizations.of(context);
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: Padding(
|
||||
// S5: the prototype's own header padding is title-tier on the
|
||||
// left, row-tier on the right (t4 e.g. Alarmas
|
||||
// `padding:0 12px 0 20px`).
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
PluriLayout.titleHorizontal,
|
||||
0,
|
||||
PluriLayout.rowHorizontal,
|
||||
0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: type.sectionTitle,
|
||||
final topInset = MediaQuery.paddingOf(context).top;
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: topInset),
|
||||
child: SizedBox(
|
||||
height: height,
|
||||
child: Padding(
|
||||
key: const ValueKey('pluri-root-header-content'),
|
||||
// S5: the prototype's own header padding is title-tier on the
|
||||
// left, row-tier on the right (t4 e.g. Alarmas
|
||||
// `padding:0 12px 0 20px`).
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
PluriLayout.titleHorizontal,
|
||||
0,
|
||||
PluriLayout.rowHorizontal,
|
||||
0,
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: type.sectionTitle,
|
||||
),
|
||||
),
|
||||
),
|
||||
...actions,
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bedtime_outlined),
|
||||
tooltip: l10n.sleepTimer,
|
||||
onPressed: onSleepTimer,
|
||||
),
|
||||
],
|
||||
...actions,
|
||||
IconButton(
|
||||
icon: const Icon(Icons.bedtime_outlined),
|
||||
tooltip: l10n.sleepTimer,
|
||||
onPressed: onSleepTimer,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user