406 lines
15 KiB
Dart
406 lines
15 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
import 'camion_estado.dart';
|
|
|
|
// ─── INSTANCIA GLOBAL ───────────────────────────────────────────
|
|
final FlutterLocalNotificationsPlugin notificationsPlugin =
|
|
FlutterLocalNotificationsPlugin();
|
|
|
|
// ─── INICIALIZAR ────────────────────────────────────────────────
|
|
Future<void> initNotifications() async {
|
|
const AndroidInitializationSettings androidSettings =
|
|
AndroidInitializationSettings('@mipmap/ic_launcher');
|
|
|
|
const InitializationSettings settings =
|
|
InitializationSettings(android: androidSettings);
|
|
|
|
await notificationsPlugin.initialize(settings);
|
|
}
|
|
|
|
// ─── 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);
|
|
}
|
|
|
|
void _actualizar() {
|
|
if (mounted) setState(() {});
|
|
}
|
|
|
|
@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),
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
} |