Files
pluriwave/lib/pantallas/pantalla_bienvenida.dart
T

189 lines
6.6 KiB
Dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../estado/estado_navegacion.dart';
import '../l10n/gen/app_localizations.dart';
import '../servicios/servicio_bienvenida.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). Both
/// surfaces coexist: [mostrarSiProcede] (WU17b) is what actually wires this
/// screen into the genuine first-launch flow, called from `app.dart` BEFORE
/// `PluriOnboardingDialog.mostrarSiProcede` on every cold start, so the two
/// never race — the once-ever welcome resolves first, then the recurring
/// what's-new dialog runs its own unrelated per-version check exactly as
/// before.
class PantallaBienvenida extends StatelessWidget {
const PantallaBienvenida({super.key});
static final ServicioBienvenida _servicio = ServicioBienvenida();
/// WU17b: shows this screen once, on the genuine first launch, then never
/// again. Mirrors `PluriOnboardingDialog.mostrarSiProcede`'s shape
/// (check-then-show-then-mark-seen) so both first-launch surfaces share
/// the same call convention from `app.dart`.
static Future<void> mostrarSiProcede(BuildContext context) async {
if (!await _servicio.debeMostrarBienvenida()) return;
if (!context.mounted) return;
await Navigator.of(
context,
).push(MaterialPageRoute<void>(builder: (_) => const PantallaBienvenida()));
await _servicio.marcarBienvenidaVista();
}
@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),
),
),
],
),
),
],
);
}
}