modificaciones en el sistema

This commit is contained in:
Kimberly
2026-05-23 05:54:34 -06:00
parent 32fe398f4b
commit cca83f0012
8 changed files with 867 additions and 533 deletions

View File

@@ -1,53 +1,406 @@
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'camion_estado.dart';
class NotificacionesService {
// Instancia única (Singleton)
static final NotificacionesService _instance = NotificacionesService._internal();
factory NotificacionesService() => _instance;
NotificacionesService._internal();
// ─── INSTANCIA GLOBAL ───────────────────────────────────────────
final FlutterLocalNotificationsPlugin notificationsPlugin =
FlutterLocalNotificationsPlugin();
final FlutterLocalNotificationsPlugin _localNotificationsPlugin = FlutterLocalNotificationsPlugin();
// ─── INICIALIZAR ────────────────────────────────────────────────
Future<void> initNotifications() async {
const AndroidInitializationSettings androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
// Inicializa el sistema de alertas del teléfono
Future<void> inicializarNotificaciones() async {
const AndroidInitializationSettings androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher'); // Icono por defecto de tu app Android
const InitializationSettings settings =
InitializationSettings(android: androidSettings);
const InitializationSettings initSettings = InitializationSettings(
android: androidSettings,
);
await notificationsPlugin.initialize(settings);
}
// SOLUCIÓN: En las versiones actuales se debe pasar como argumento con nombre 'settings:'
await _localNotificationsPlugin.initialize(
initSettings,
// Si en un futuro necesitas reaccionar cuando el usuario presiona la notificación:
// onDidReceiveNotificationResponse: (NotificationResponse response) { ... }
);
// ─── ENVIAR NOTIFICACIÓN ────────────────────────────────────────
Future<void> enviarNotificacion({
required int id,
required String titulo,
required String cuerpo,
}) async {
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'canal_camion',
'Camión Recolector',
channelDescription: 'Notificaciones del camión recolector',
importance: Importance.max,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
);
const NotificationDetails details =
NotificationDetails(android: androidDetails);
await notificationsPlugin.show(id, titulo, cuerpo, details);
}
// ════════════════════════════════════════════════════════════════
// PANTALLA
// ════════════════════════════════════════════════════════════════
class AlertasNotificacionesScreen extends StatefulWidget {
final bool notificarInicioRuta;
final bool notificarAproximacion;
final bool notificarRetrasosFallas;
const AlertasNotificacionesScreen({
super.key,
required this.notificarInicioRuta,
required this.notificarAproximacion,
required this.notificarRetrasosFallas,
});
@override
State<AlertasNotificacionesScreen> createState() =>
_AlertasNotificacionesScreenState();
}
class _AlertasNotificacionesScreenState
extends State<AlertasNotificacionesScreen> {
late bool notificarInicioRuta;
late bool notificarCamionCerca;
late bool notificarRetrasos;
bool privacidadActiva = true;
@override
void initState() {
super.initState();
notificarInicioRuta = widget.notificarInicioRuta;
notificarCamionCerca = widget.notificarAproximacion;
notificarRetrasos = widget.notificarRetrasosFallas;
// Sincroniza switches con el estado global
camionEstado.notificarInicioRuta = notificarInicioRuta;
camionEstado.notificarCamionCerca = notificarCamionCerca;
camionEstado.notificarRetrasos = notificarRetrasos;
// Escucha cambios del camión para actualizar UI
camionEstado.addListener(_actualizar);
}
// Método genérico para disparar la alerta nativa en Android/iOS
Future<void> mostrarNotificacionPush({
required int id,
required String titulo,
required String mensaje,
}) async {
const AndroidNotificationDetails androidDetails = AndroidNotificationDetails(
'canal_alertas_operativas', // ID del canal
'Alertas Operativas Camión', // Nombre del canal visible para el usuario
channelDescription: 'Avisos de rutas, aproximaciones e imprevistos mecánicos',
importance: Importance.max,
priority: Priority.high,
playSound: true,
);
const NotificationDetails platformDetails = NotificationDetails(
android: androidDetails,
);
void _actualizar() {
if (mounted) setState(() {});
}
await _localNotificationsPlugin.show(
id,
titulo,
mensaje,
platformDetails,
@override
void dispose() {
camionEstado.removeListener(_actualizar);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[100],
appBar: AppBar(
title: const Text('Alertas y Notificaciones'),
backgroundColor: Colors.green,
foregroundColor: Colors.white,
),
body: ListView(
padding: const EdgeInsets.all(16),
children: [
// ── TARJETA SIMULADOR ─────────────────────────────
Card(
elevation: 4,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.local_shipping, color: Colors.green, size: 30),
SizedBox(width: 10),
Text(
'Estado del Camión',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 20),
// Barra de progreso
ClipRRect(
borderRadius: BorderRadius.circular(8),
child: LinearProgressIndicator(
value: camionEstado.positionId / 8,
minHeight: 12,
backgroundColor: Colors.green[100],
valueColor: const AlwaysStoppedAnimation<Color>(
Colors.green,
),
),
),
const SizedBox(height: 12),
// Etapa actual
Container(
width: double.infinity,
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius: BorderRadius.circular(12),
),
child: Text(
CamionEstado.etapas[camionEstado.positionId] ?? '',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
),
),
),
const SizedBox(height: 8),
Text(
'Posición: ${camionEstado.positionId} / 8',
style: TextStyle(color: Colors.grey[600], fontSize: 13),
),
const SizedBox(height: 16),
// Botones — usan el timer global
Row(
children: [
Expanded(
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: camionEstado.corriendo
? Colors.grey
: Colors.green,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: camionEstado.corriendo
? null
: camionEstado.iniciarTimer,
icon: const Icon(Icons.play_arrow),
label: const Text('Iniciar'),
),
),
const SizedBox(width: 12),
Expanded(
child: OutlinedButton.icon(
style: OutlinedButton.styleFrom(
foregroundColor: Colors.green,
side: const BorderSide(color: Colors.green),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
),
),
onPressed: camionEstado.reiniciar,
icon: const Icon(Icons.refresh),
label: const Text('Reiniciar'),
),
),
],
),
],
),
),
),
const SizedBox(height: 20),
// ── TARJETA ALERTAS ───────────────────────────────
Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.notifications_active,
color: Colors.green, size: 30),
SizedBox(width: 10),
Text(
'Configuración de Alertas',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 20),
SwitchListTile(
value: notificarInicioRuta,
activeColor: Colors.green,
title: const Text('Inicio de Ruta'),
subtitle: const Text(
'Recibir aviso cuando el camión inicie su recorrido.',
),
secondary: const Icon(Icons.route),
onChanged: (value) {
setState(() => notificarInicioRuta = value);
camionEstado.notificarInicioRuta = value;
},
),
const Divider(),
SwitchListTile(
value: notificarCamionCerca,
activeColor: Colors.green,
title: const Text('Camión Cercano'),
subtitle: const Text(
'Notificación cuando el camión esté próximo a tu domicilio.',
),
secondary: const Icon(Icons.local_shipping),
onChanged: (value) {
setState(() => notificarCamionCerca = value);
camionEstado.notificarCamionCerca = value;
},
),
const Divider(),
SwitchListTile(
value: notificarRetrasos,
activeColor: Colors.green,
title: const Text('Retrasos o Fallas'),
subtitle: const Text(
'Avisos por tráfico, retrasos o fallas mecánicas.',
),
secondary: const Icon(Icons.warning_amber_rounded),
onChanged: (value) {
setState(() => notificarRetrasos = value);
camionEstado.notificarRetrasos = value;
},
),
],
),
),
),
const SizedBox(height: 20),
// ── TARJETA PRIVACIDAD ────────────────────────────
Card(
elevation: 3,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(18),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Row(
children: [
Icon(Icons.security, color: Colors.redAccent, size: 30),
SizedBox(width: 10),
Text(
'Privacidad y Seguridad',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
],
),
const SizedBox(height: 20),
SwitchListTile(
value: privacidadActiva,
activeColor: Colors.green,
title: const Text('Protección Anti-Snooping'),
subtitle: const Text(
'Impide explorar rutas, domicilios o información de otros usuarios.',
),
secondary: const Icon(Icons.lock_outline),
onChanged: (value) =>
setState(() => privacidadActiva = value),
),
const SizedBox(height: 15),
Container(
padding: const EdgeInsets.all(14),
decoration: BoxDecoration(
color: Colors.green[50],
borderRadius: BorderRadius.circular(12),
),
child: const Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Icon(Icons.verified_user, color: Colors.green),
SizedBox(width: 10),
Expanded(
child: Text(
'El sistema solo mostrará información relacionada con los domicilios registrados por el usuario autenticado.',
style: TextStyle(fontSize: 14),
),
),
],
),
),
],
),
),
),
const SizedBox(height: 25),
// ── BOTÓN GUARDAR ─────────────────────────────────
SizedBox(
height: 55,
child: ElevatedButton.icon(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
),
onPressed: () {
Navigator.pop(context, {
'inicio' : notificarInicioRuta,
'aproximacion': notificarCamionCerca,
'retrasos' : notificarRetrasos,
});
},
icon: const Icon(Icons.save),
label: const Text(
'Guardar Configuración',
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
),
),
],
),
);
}
}