Build the first-run welcome surface from mockup screen 14, stripped of its entire monetization block: no PRO pill, no "14 dias PRO gratis" trial line, no pricing card, no secondary "free version" link. Ships only logo, headline, body copy, exactly 3 feature bullets, and the single "Empezar a escuchar" CTA, as a full-screen route (not a modal). Spanish copy is re-cast from the mockup's "tu" form to the app's established voseo register (matching ~750 existing app_es.arb lines), using "auto" instead of "coche" per the one existing precedent. The 3 bullet icons reuse existing tokens (electricMagenta/liveGreen/ warmCoral) that already match the mockup's own hex values for them. CTA switches to the Escuchar tab and pops the route. Wiring this screen into the real first-launch flow (main.dart/app.dart) is left for a follow-up unit, same shape as WU15/WU15b - this WU only covers the isolated, tested screen per its own task list and verify command. WU17.
170 lines
5.7 KiB
Dart
170 lines
5.7 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import '../estado/estado_navegacion.dart';
|
|
import '../l10n/gen/app_localizations.dart';
|
|
import '../tema/pluriwave_theme.dart';
|
|
|
|
/// WU17: first-run welcome surface (`onboarding-welcome` spec, mockup
|
|
/// screen 14) rebuilt WITHOUT its monetization content — no "PRO" pill, no
|
|
/// "14 días" trial line, no pricing card, and no secondary "free version"
|
|
/// link (binding no-monetization requirement). Content only: logo,
|
|
/// headline, body copy, exactly 3 feature bullets, and the single
|
|
/// "Empezar a escuchar" CTA.
|
|
///
|
|
/// A full-screen ROUTE, not a modal `Dialog` — deliberately distinct from
|
|
/// the pre-existing `PluriOnboardingDialog` (an unrelated "what's new"
|
|
/// help-content modal already shown from `app.dart`'s launch flow). This
|
|
/// work unit's own task list and verify command scope to this screen in
|
|
/// isolation; wiring it into the real first-launch flow (i.e. deciding
|
|
/// when `app.dart`/`main.dart` should push it) is not part of WU17.
|
|
class PantallaBienvenida extends StatelessWidget {
|
|
const PantallaBienvenida({super.key});
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final l10n = AppLocalizations.of(context);
|
|
final t = context.pluriTokens;
|
|
final theme = Theme.of(context);
|
|
|
|
return Scaffold(
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: const EdgeInsets.fromLTRB(24, 48, 24, 28),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: [
|
|
Image.asset(
|
|
'assets/icons/pluriwave_app_mark.png',
|
|
width: 76,
|
|
height: 76,
|
|
errorBuilder:
|
|
(_, __, ___) => Icon(
|
|
Icons.graphic_eq_rounded,
|
|
size: 76,
|
|
color: t.electricMagenta,
|
|
),
|
|
),
|
|
const SizedBox(height: 22),
|
|
Text(
|
|
l10n.welcomeHeadline,
|
|
style: theme.textTheme.headlineMedium?.copyWith(
|
|
fontWeight: FontWeight.w800,
|
|
height: 1.05,
|
|
letterSpacing: -1.0,
|
|
),
|
|
),
|
|
const SizedBox(height: 12),
|
|
Text(
|
|
l10n.welcomeBody,
|
|
style: theme.textTheme.bodyMedium?.copyWith(
|
|
color: theme.colorScheme.onSurface.withValues(alpha: 0.72),
|
|
height: 1.5,
|
|
),
|
|
),
|
|
const SizedBox(height: 28),
|
|
FilaCaracteristicaBienvenida(
|
|
icon: Icons.equalizer_rounded,
|
|
iconColor: t.electricMagenta,
|
|
titulo: l10n.welcomeBullet1Title,
|
|
subtitulo: l10n.welcomeBullet1Subtitle,
|
|
),
|
|
const SizedBox(height: 16),
|
|
FilaCaracteristicaBienvenida(
|
|
icon: Icons.directions_car_rounded,
|
|
iconColor: t.liveGreen,
|
|
titulo: l10n.welcomeBullet2Title,
|
|
subtitulo: l10n.welcomeBullet2Subtitle,
|
|
),
|
|
const SizedBox(height: 16),
|
|
FilaCaracteristicaBienvenida(
|
|
icon: Icons.alarm_rounded,
|
|
iconColor: t.warmCoral,
|
|
titulo: l10n.welcomeBullet3Title,
|
|
subtitulo: l10n.welcomeBullet3Subtitle,
|
|
),
|
|
const SizedBox(height: 32),
|
|
SizedBox(
|
|
height: 58,
|
|
child: FilledButton(
|
|
onPressed: () => _empezar(context),
|
|
child: Text(
|
|
l10n.welcomeCtaLabel,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
void _empezar(BuildContext context) {
|
|
context.read<EstadoNavegacionRaiz>().irA(RaizPluriWave.escuchar);
|
|
Navigator.of(context).pop();
|
|
}
|
|
}
|
|
|
|
/// One icon-badge + title/subtitle row. Public (not `_FilaCaracteristica`)
|
|
/// so the structural "exactly 3 feature bullets" guard in
|
|
/// `pantalla_bienvenida_test.dart` can target it via `find.byType` — the
|
|
/// same reason WU4 made `FormularioEmisoraPersonalizada` public.
|
|
class FilaCaracteristicaBienvenida extends StatelessWidget {
|
|
const FilaCaracteristicaBienvenida({
|
|
super.key,
|
|
required this.icon,
|
|
required this.iconColor,
|
|
required this.titulo,
|
|
required this.subtitulo,
|
|
});
|
|
|
|
final IconData icon;
|
|
final Color iconColor;
|
|
final String titulo;
|
|
final String subtitulo;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
return Row(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Container(
|
|
width: 38,
|
|
height: 38,
|
|
decoration: BoxDecoration(
|
|
color: iconColor.withValues(alpha: 0.16),
|
|
borderRadius: BorderRadius.circular(11),
|
|
),
|
|
child: Icon(icon, size: 20, color: iconColor),
|
|
),
|
|
const SizedBox(width: 13),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
titulo,
|
|
style: theme.textTheme.bodyMedium?.copyWith(
|
|
fontWeight: FontWeight.w800,
|
|
),
|
|
),
|
|
Text(
|
|
subtitulo,
|
|
style: theme.textTheme.bodySmall?.copyWith(
|
|
color: theme.colorScheme.onSurface.withValues(alpha: 0.58),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|