modificaciones en el sistema
This commit is contained in:
@@ -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),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user