feat(app): add onboarding and harden alarms
Build & Deploy Pluriwave / Análisis de código (push) Successful in 21s
Build & Deploy Pluriwave / Build APK + AAB release (push) Failing after 1m6s

This commit is contained in:
2026-05-23 01:22:37 +02:00
parent 27b8fccac9
commit 896349ad5f
44 changed files with 1772 additions and 241 deletions
+198
View File
@@ -0,0 +1,198 @@
import 'package:flutter/material.dart';
import '../servicios/servicio_contenido_app.dart';
import 'pluri_glass_surface.dart';
import 'pluri_markdown.dart';
class PluriOnboardingDialog {
PluriOnboardingDialog._();
static final _servicio = ServicioContenidoApp();
static Future<void> mostrarSiProcede(BuildContext context) async {
if (!await _servicio.debeMostrarInicio()) return;
if (!context.mounted) return;
await mostrar(context, soloPendientes: true);
await _servicio.marcarVisto();
}
static Future<void> mostrar(
BuildContext context, {
bool soloPendientes = false,
}) async {
final idioma = Localizations.localeOf(context).languageCode;
final contenido = await _servicio.cargar(
idioma,
soloPendientes: soloPendientes,
);
if (!context.mounted) return;
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (_) => _PluriOnboardingContent(contenido: contenido),
);
}
}
class _PluriOnboardingContent extends StatelessWidget {
const _PluriOnboardingContent({required this.contenido});
final ContenidoAyudaPluri contenido;
@override
Widget build(BuildContext context) {
final labels = _labels(Localizations.localeOf(context).languageCode);
final size = MediaQuery.sizeOf(context);
return Dialog(
insetPadding: const EdgeInsets.all(16),
backgroundColor: Colors.transparent,
child: ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 720,
maxHeight: size.height * 0.86,
),
child: PluriGlassSurface(
borderRadius: BorderRadius.circular(32),
glowColor: Theme.of(
context,
).colorScheme.primary.withValues(alpha: 0.28),
child: Column(
children: [
Row(
children: [
Container(
width: 54,
height: 54,
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primary,
Theme.of(context).colorScheme.tertiary,
],
),
),
child: const Icon(
Icons.graphic_eq_rounded,
color: Colors.white,
),
),
const SizedBox(width: 14),
Expanded(
child: Text(
labels.title,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w900,
),
),
),
IconButton(
tooltip: labels.close,
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.close_rounded),
),
],
),
const SizedBox(height: 12),
Expanded(
child: ListView(
children: [
if (contenido.onboarding.trim().isNotEmpty)
PluriMarkdown(contenido.onboarding),
if (contenido.notas.isNotEmpty) ...[
const SizedBox(height: 18),
Text(
labels.news,
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.w900,
),
),
const SizedBox(height: 8),
for (final nota in contenido.notas)
ExpansionTile(
tilePadding: EdgeInsets.zero,
title: Text('v${nota.version}'),
subtitle:
nota.resumen.isEmpty ? null : Text(nota.resumen),
children: [
Padding(
padding: const EdgeInsets.only(bottom: 12),
child: PluriMarkdown(nota.markdown),
),
],
),
],
],
),
),
const SizedBox(height: 12),
Align(
alignment: Alignment.centerRight,
child: FilledButton.icon(
onPressed: () => Navigator.of(context).pop(),
icon: const Icon(Icons.check_rounded),
label: Text(labels.start),
),
),
],
),
),
),
);
}
}
_OnboardingLabels _labels(String languageCode) {
return switch (languageCode) {
'es' => const _OnboardingLabels(
title: 'Bienvenido a PluriWave',
news: 'Novedades',
start: 'Empezar',
close: 'Cerrar',
),
'fr' => const _OnboardingLabels(
title: 'Bienvenue sur PluriWave',
news: 'Nouveautés',
start: 'Commencer',
close: 'Fermer',
),
'de' => const _OnboardingLabels(
title: 'Willkommen bei PluriWave',
news: 'Neuigkeiten',
start: 'Starten',
close: 'Schließen',
),
'it' => const _OnboardingLabels(
title: 'Benvenuto in PluriWave',
news: 'Novità',
start: 'Inizia',
close: 'Chiudi',
),
'pt' => const _OnboardingLabels(
title: 'Bem-vindo ao PluriWave',
news: 'Novidades',
start: 'Começar',
close: 'Fechar',
),
_ => const _OnboardingLabels(
title: 'Welcome to PluriWave',
news: 'Whats new',
start: 'Start',
close: 'Close',
),
};
}
class _OnboardingLabels {
const _OnboardingLabels({
required this.title,
required this.news,
required this.start,
required this.close,
});
final String title;
final String news;
final String start;
final String close;
}