
Caso de estudio: SGU-ASE Aplicativo para servicios viales para aseguradoras.
Case study data record.
Tech_Stack / Tags
Meta
Caso de Estudio: SGU-Ase
Plataforma de Asistencia Vial con Despacho Inteligente
Resumen
SGU-Ase es una plataforma móvil de asistencia vial desarrollada para una empresa del sector asegurador que opera su propia flota de conductores. La aplicación conecta en tiempo real a clientes que necesitan servicios de emergencia vial con conductores disponibles, incorporando un sistema de despacho inteligente, seguimiento GPS en vivo, historial de viajes auditables y precios transparentes. Construida en Flutter para despliegue multiplataforma, la solución unifica tres experiencias de usuario —cliente, conductor y administrador— bajo una misma base de código con backend serverless en Supabase.
1. El Problema
Contexto del cliente
La empresa opera en el sector de asistencia vial con dos frentes: por un lado, presta servicios a aseguradoras como proveedor externo y, por otro, gestiona su propia flota de conductores para servicios directos. Antes del desarrollo de SGU-Ase, la operación dependía de llamadas telefónicas, comunicación por whatsapp o mensaje y se hacen los registros manualmente. Esto generaba varios puntos de fricción críticos:
Lentitud en la respuesta al cliente. Solicitar un servicio implicaba una cadena de llamadas: cliente → call center → despachador → conductor. Cada eslabón añadía minutos que, en una emergencia vial, cuentan.
Visibilidad nula para el cliente. Una vez solicitado el servicio, el cliente no sabía dónde estaba el conductor, cuánto tardaría ni si su solicitud había sido asignada. La incertidumbre generaba ansiedad y llamadas repetidas al call center.
Falta de trazabilidad para auditorías. Las aseguradoras exigen registros detallados de cada servicio: hora de inicio, hora de finalización, duración, precio, kilometraje. Sin un sistema digital, conciliar facturas era un proceso manual propenso a errores.
Despacho ineficiente. Sin un sistema que mostrara qué conductores estaban disponibles y dónde, el despachador asignaba servicios basándose en intuición más que en proximidad real, resultando en tiempos de llegada subóptimos.
Cero datos para mejorar. Sin registros digitales, la empresa no podía medir tiempos de respuesta, identificar cuellos de botella ni optimizar la distribución de su flota.
Perfiles de usuario
| Perfil | Necesidad principal |
|---|---|
| Cliente | Solicitar servicios rápido, ver quién viene y cuánto tarda, tener historial de viajes |
| Conductor | Recibir solicitudes, navegar al punto de servicio, registrar tiempos y checklist |
| Administrador | Ver flota en tiempo real, gestionar usuarios, generar reportes para aseguradoras |
2. La Solución: Arquitectura y Stack
SGU-Ase se desarrolló como una aplicación Flutter multiplataforma (Android, iOS, Web) con un backend serverless. La decisión de usar Flutter respondió a la necesidad de cubrir todos los dispositivos del ecosistema —teléfonos de clientes iOS/Android, tablets de administradores y potencialmente PDAs en vehículos— con una sola base de código.
Stack técnico
┌──────────────────────────────────────────┐
│ CLIENTE FLUTTER │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Cliente │ │ Conductor │ │ Admin │ │
│ │ App │ │ App │ │ Panel │ │
│ └────┬────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │
│ ┌────┴───────────┴────────────┴─────┐ │
│ │ Provider (State Mgmt) │ │
│ └────────────────┬──────────────────┘ │
│ │ │
│ ┌────────────────┴──────────────────┐ │
│ │ Servicios (Facade Pattern) │ │
│ │ RequestService → CRUD │ Dispatch │ │
│ │ → Queue │ Track │ │
│ └────────────────┬──────────────────┘ │
└───────────────────┼──────────────────────┘
│
┌──────────────┴──────────────┐
│ Supabase │
│ ┌────────┐ ┌───────────┐ │
│ │ Auth │ │ Realtime │ │
│ │ (Row │ │ (WebSocket │ │
│ │ Level │ │ Changes) │ │
│ │ Sec) │ │ │ │
│ └────────┘ └───────────┘ │
│ ┌────────────────────────┐ │
│ │ PostgreSQL + │ │
│ │ PostGIS (spatial) │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
│
┌──────────────┴──────────────┐
│ Servicios externos │
│ ┌──────────┐ ┌──────────┐ │
│ │ Firebase │ │ Google │ │
│ │ FCM │ │ Maps │ │
│ │ (Push) │ │ (Routes) │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ │
│ │ OSRM │ (fallback) │
│ │ Routes │ │
│ └──────────┘ │
└──────────────────────────────┘
Dependencias clave
| Paquete | Versión | Función |
|---|---|---|
supabase_flutter |
^2.8.0 | Auth, base de datos, tiempo real |
google_maps_flutter |
^2.14.0 | Mapas y visualización de rutas |
firebase_messaging |
^16.1.0 | Notificaciones push |
provider |
^6.1.5 | Gestión de estado |
connectivity_plus |
^7.0.0 | Detección de conectividad |
flutter_foreground_task |
^8.13.4 | Servicio en segundo plano para ETA |
geolocator |
^14.0.2 | Posicionamiento GPS |
flutter_local_notifications |
^18.0.1 | Notificaciones locales |
Base de datos Supabase
El modelo de datos gira en torno a dos tablas principales más varias auxiliares para el sistema de despacho:
users— extiendeauth.userscon rol (client,driver,admin), disponibilidad, ubicación GPS (PostGIS), datos del vehículo y token FCM.service_requests— registra cada solicitud con tipo de servicio (Bateria,neumático,gasolina,remolcar,mecánico,cerrajero), ubicación, estado, precio, marcas de tiempo de inicio/fin y campos de cancelación.request_queues— tabla de despacho que gestiona invitaciones a conductores, aceptaciones y rechazos con timestamp.ratings— calificaciones post-servicio para control de calidad.driver_location— persistencia periódica de ubicación para recuperación tras crash.
La base de datos aprovecha PostGIS para consultas espaciales (conductores cercanos) y Row Level Security para control de acceso a nivel de fila.
3. Funcionalidades Principales
3.1 Cliente: solicitud y seguimiento en tiempo real
El flujo del cliente es sencillo e intencionalmente rápido:
- Solicitud en dos toques. El cliente selecciona el tipo de servicio (
batería, llanta, gasolina, grúa, mecánico, cerrajero) y confirma la ubicación. La app detecta automáticamente su posición GPS. - Despacho automático. El sistema busca conductores disponibles en un radio de 15 km usando consultas espaciales PostGIS y les envía la solicitud.
- Seguimiento en vivo. Una vez aceptado, el cliente ve al conductor en el mapa con ruta, ETA calculada por Google Directions API, y recibe notificaciones en cada cambio de estado (aceptado → en camino → llegó → servicio iniciado → completado).
- Historial de viajes. Cada servicio queda registrado con todos los timestamps, precio y tipo, accesible desde el perfil del cliente para auditorías de la aseguradora.
Archivos clave:
lib/screens/client/client_home_screen.dart(604 líneas)lib/screens/client/controllers/client_request_controller.dart(634 líneas)lib/screens/client/controllers/client_tracking_controller.dart(705 líneas)lib/screens/client/widgets/client_eta_overlay.dart(450 líneas)
3.2 Conductor: recepción y gestión de servicios
La experiencia del conductor está optimizada para uso durante la conducción:
- Notificación de solicitud entrante. Recibe una notificación push (FCM) y una overlay en la app con los detalles del servicio y la distancia estimada.
- Aceptación/rechazo. El conductor puede aceptar o rechazar. Si rechaza, el sistema automáticamente busca al siguiente conductor disponible en la cola.
- Navegación GPS. Una vez aceptado, la app muestra la ruta hasta el cliente con indicaciones visuales y seguimiento de posición en tiempo real mediante broadcast de ubicación.
- Checklist de servicio. El conductor completa un checklist digital (llegada, diagnóstico, trabajo realizado, fotos) sincronizado en tiempo real vía Supabase Realtime, visible por el administrador.
- Cierre de servicio. Registra el tiempo de finalización, calcula la duración y libera al conductor para nuevas solicitudes.
Archivos clave:
lib/screens/driver/driver_home_screen.dart(609 líneas)lib/screens/driver/controllers/driver_trip_controller.dart(410 líneas)lib/screens/driver/controllers/driver_offer_controller.dart(324 líneas)lib/screens/driver/widgets/driver_bottom_sheet.dart(780 líneas)lib/screens/driver/widgets/driver_checklist_sheet.dart(898 líneas)
3.3 Administrador: panel de control
- Vista general de flota. Mapa con todos los conductores activos y su estado (disponible, en servicio).
- Gestión de usuarios. Altas, bajas, promoción de usuarios a conductor o administrador.
- Reportes. Preparado para generación de reportes de tiempos, precios y volumen de servicios para aseguradoras.
Archivos clave:
lib/screens/admin/admin_home_screen.dart(79 líneas)lib/screens/admin/tabs/admin_overview_tab.dart(158 líneas)lib/screens/admin/tabs/admin_users_tab.dart(96 líneas)
4. Decisiones Técnicas Clave
4.1 Arquitectura por controladores
En lugar de meter toda la lógica en los widgets, SGU-Ase separa cada responsabilidad en controladores dedicados que se comunican mediante el patrón listener:
ClientHomeScreen
├── ClientLocationController → GPS, permisos, posición actual
├── ClientRequestController → crear/cancelar solicitudes
└── ClientTrackingController → seguir conductor, ETA, animaciones
DriverHomeScreen
├── DriverLocationController → GPS, broadcast de ubicación
├── DriverOfferController → recibir/rechazar ofertas
├── DriverTripController → gestionar viaje activo
└── DriverQueueController → cola de invitaciones
Esto permite probar cada componente de forma aislada y facilita el mantenimiento en pantallas que superan las 600 líneas.
4.2 Patrón Facade en servicios
RequestService actúa como fachada sobre cuatro servicios internos:
| Sub-servicio | Responsabilidad |
|---|---|
RequestCrudService |
CRUD de solicitudes, cambios de estado |
DispatchService |
Búsqueda de conductores por proximidad |
QueueService |
Sistema de cola con invitaciones y ciclado |
TrackingService |
Heartbeat, broadcast de ubicación, suscripciones |
El código legacy sigue funcionando sin cambios; el nuevo código puede importar directamente el sub-servicio que necesita.
4.3 Sistema de cancelación con tres actores
Uno de los flujos más complejos del sistema es la cancelación, que puede originarse en tres fuentes distintas con comportamientos diferentes:
| Actor | Comportamiento |
|---|---|
| Cliente | Cancela la solicitud. Se notifica al conductor. Se cancelan todas las entradas de cola. |
| Conductor | Cancela su asignación. El sistema marca la solicitud como cancelada por conductor y automáticamente re-despacha una nueva solicitud para el cliente, preservando el historial de la original para auditoría. |
| Sistema | Timeout por falta de conductores o expiración. Notifica a ambas partes. |
Este flujo está implementado en CancellationService (324 líneas) con notificaciones push vía Edge Function de Supabase.
4.4 Estrategia de rutas con fallback en cascada
El cálculo de ETA y ruta tiene tres niveles de fallback para garantizar que siempre se muestre información al usuario, incluso sin conexión a Google Maps:
- Google Directions API — cálculo preciso con tráfico real, polilínea de ruta
- OSRM (Open Source Routing Machine) — API pública gratuita como respaldo
- Fórmula de Haversine — cálculo en línea recta con factor de corrección urbano (×1.35) para estimar distancia real por carretera
Las polilíneas de más de 500 puntos se decodifican en un isolate de Dart para no bloquear el hilo de UI. Los resultados se cachean por 5 minutos con clave basada en coordenadas redondeadas a 4 decimales (~11 m de precisión).
Fragmento relevante (lib/services/routes_service.dart):
// Road distance is typically 1.35× the straight-line distance in urban areas
final roadMeters = straightMeters * 1.35;
// Estimate at 30 km/h average
final minutes = (roadMeters / (30000 / 60)).ceil();
4.5 Broadcast de ubicación en tiempo real
Para el seguimiento del conductor en el mapa del cliente, se usa el canal de broadcast de Supabase Realtime en lugar de actualizaciones frecuentes a la base de datos. Esto reduce la carga en PostgreSQL: las escrituras se limitan a un heartbeat periódico para recuperación ante crashes, mientras que la ubicación en vivo viaja por WebSocket sin pasar por la DB.
// Broadcast-based real-time location (driver side)
Future<void> startLocationBroadcast(String requestId);
Future<void> broadcastDriverLocation(double lat, double lng);
Future<void> stopLocationBroadcast();
4.6 Checklist sincronizado en tiempo real
El progreso del servicio se registra mediante un checklist digital que usa una columna JSONB en service_requests. Cada vez que el conductor marca un ítem, el cambio se propaga vía Supabase Realtime al administrador y al cliente. Esto elimina la necesidad de llamadas de estado y proporciona trazabilidad completa del servicio.
4.7 Optimización de arranque en frío
Se identificó que Firebase y Supabase se inicializaban secuencialmente, añadiendo latencia al arranque. La solución fue paralelizar ambas inicializaciones:
await Future.wait([
Firebase.initializeApp(),
Supabase.initialize(url: '...', anonKey: '...'),
]);
Además, se precargan los iconos de marcador del mapa en un cache para evitar el parpadeo de 200-500ms en la primera carga del mapa.
5. Estructura del Proyecto
lib/
├── main.dart # Entry point, auth wrapper, routing por rol
├── models/ # Modelos de datos
│ ├── user_model.dart # AppUser con rol, vehículo, disponibilidad
│ ├── service_request_model.dart # ServiceRequest con estados, cancelación, checklist
│ ├── checklist_progress_model.dart # Progreso de checklist
│ └── service_checklist_model.dart # Definición de checklists por tipo de servicio
├── providers/ # Estado global
│ └── auth_provider.dart # Autenticación (email, Google), estado de sesión
├── screens/
│ ├── client/ # App del cliente (≈3500 líneas)
│ │ ├── client_home_screen.dart
│ │ ├── controllers/ # 3 controladores (ubicación, solicitud, tracking)
│ │ └── widgets/ # 8 widgets (mapa, ETA, dashboard, bottom sheet…)
│ ├── driver/ # App del conductor (≈4500 líneas)
│ │ ├── driver_home_screen.dart
│ │ ├── controllers/ # 4 controladores (ubicación, ofertas, viaje, cola)
│ │ └── widgets/ # 5 widgets (mapa, bottom sheet, checklist…)
│ ├── admin/ # Panel de administración (≈500 líneas)
│ │ ├── admin_home_screen.dart
│ │ └── tabs/ # Overview, usuarios, reportes, settings
│ ├── common/ # Pantallas compartidas
│ │ ├── trip_history_screen.dart # Historial de viajes para auditoría
│ │ ├── service_completion_screens.dart
│ │ ├── user_profile_screen.dart
│ │ ├── permission_guard.dart # Guard de permisos (ubicación, notificaciones)
│ │ └── permission_onboarding_screen.dart
│ ├── login_screen.dart # Login con email y Google
│ └── register_screen.dart
├── services/ # Capa de servicios
│ ├── auth_service.dart # Auth con Supabase + Google Sign-In
│ ├── request_service.dart # Fachada sobre los 4 sub-servicios
│ ├── request/ # Sub-servicios especializados
│ │ ├── request_crud_service.dart # CRUD y ciclo de vida
│ │ ├── dispatch_service.dart # Búsqueda y despacho
│ │ ├── queue_service.dart # Cola de invitaciones
│ │ └── tracking_service.dart # Heartbeat y broadcast
│ ├── cancellation_service.dart # Flujos de cancelación (3 actores)
│ ├── checklist_service.dart # Gestión de checklists JSONB
│ ├── connectivity_service.dart # Monitoreo de conectividad
│ ├── foreground_eta_service.dart # Servicio en segundo plano
│ ├── notification_service.dart # Push notifications (FCM + locales)
│ ├── routes_service.dart # ETA y rutas con fallback en cascada
│ ├── service_request_manager.dart # Orquestador de solicitudes
│ ├── admin_service.dart # Operaciones de administrador
│ └── toast_service.dart # Notificaciones toast
├── data/ # Datos estáticos y configuración
│ └── checklist_definitions.dart # Definiciones de checklists por servicio
├── utils/ # Utilidades
│ ├── map_utils.dart # Cálculos geoespaciales
│ ├── design_tokens.dart # Tokens de diseño
│ ├── theme.dart # Tema oscuro de la app
│ ├── marker_icon_cache.dart # Cache de iconos de marcador
│ └── page_transitions.dart # Transiciones entre pantallas
└── widgets/ # Widgets reutilizables
├── incoming_request_overlay.dart # Overlay de solicitud entrante (430 líneas)
├── skeleton_loader.dart # Loaders tipo skeleton
└── app_card.dart # Componente de tarjeta
Total: 16,324 líneas de código Dart en aproximadamente 60 archivos.
6. Proceso de Desarrollo
Línea de tiempo
El historial de git documenta un desarrollo concentrado. El proyecto se construyó sobre una base sólida desde el inicio, con tres commits principales:
| Fecha | Descripción | |
|---|---|---|
| 3 Feb 2026 | Estructura inicial del proyecto Flutter | |
| 3 Feb 2026 | Versión completa con Google Maps: 212 archivos, 17,246 líneas insertadas | |
| 3 Feb 2026 | Migración a Mapbox (rama mapboxversion): 22 archivos modificados |
Rama de migración de mapas
Se creó una rama feature/mapbox-migration para evaluar la viabilidad de sustituir Google Maps por Mapbox como proveedor de mapas. Esta rama introdujo:
lib/utils/map_controller_wrapper.dart(152 líneas) — abstracción para alternar entre proveedoreslib/utils/google_maps_compat.dart(76 líneas) — capa de compatibilidadlib/models/lat_lng.dart— modelo propio de coordenadas para independencia del SDK
La migración afectó 22 archivos y demuestra una arquitectura preparada para cambiar de proveedor sin reescribir la capa de negocio, al final se dejo con google maps por cuestiones de pruebas pero la alternativa con mapbox es una alternativa mas economica a la API de Google Maps.
Estado actual
Imagenes de la app:
Vista de conductor y clientes al iniciar sesión.
### Vista de los usuarios cuando el cliente solicita un servicio y el conductor lo toma.
### vista donde el conductor sigue la ruta que la app le proporciona para llegar al destino
### Vista de los usuarios cuando ya se puede iniciar el servicio.
### vista del drive realizando el servicios y marcando su checklist
### Vista del servicio en progreso dentro de la app.
### Vista del conductor donde toma foto del problema segun servicio indique para tener evidencias del problema.
### Finalización del servicio.
### Vista del pago y encuesta para poder calificar al tecnico.
### Historial de servicios que el driver presenta.
Actualmente la app se encuentra en pruebas internas para ir tocando detalles para mejorar la experiencias para cualquiera de las partes afinar detalles que se presentan en el camino.
Patrones de calidad visibles en el código
- Manejo de errores con reintentos. El
AuthProviderimplementa 3 reintentos con backoff de 1 segundo para la carga del perfil de usuario. - Prevención de memory leaks. Los listeners se almacenan como referencias para removerlos en
dispose(). - Optimización de reconstrucción. Uso de
Selectoren Provider para evitar rebuilds innecesarios. - Graceful degradation. El sistema de rutas tiene 3 niveles de fallback; las notificaciones tienen fallback local si FCM no está disponible.
- SQL organizado. El proyecto incluye 20+ scripts SQL para migraciones, debugging, corrección de RLS y administración de usuarios.
7. Resultados
Para el cliente final (la empresa)
- Reducción del tiempo de respuesta. La eliminación de la cadena de llamadas y el despacho automático por proximidad reduce el tiempo desde la solicitud hasta la asignación a segundos.
- Trazabilidad completa. Cada servicio queda registrado con timestamps, ubicación, checklist de trabajo y precio, facilitando auditorías de aseguradoras.
- Visibilidad de flota. El panel de administración muestra en tiempo real la ubicación y estado de todos los conductores.
- Escalabilidad operativa. El sistema de cola de despacho permite manejar picos de demanda sin intervención manual.
Para los usuarios finales
- Experiencia de cliente. Solicitar un servicio toma menos de 30 segundos. El seguimiento en vivo reduce la ansiedad y las llamadas al call center.
- Experiencia de conductor. Las notificaciones push y la navegación integrada eliminan la dependencia de llamadas de radio. El checklist digital documenta el trabajo realizado.
Métricas del código
| Métrica | Valor |
|---|---|
| Líneas de código Dart | 16,324 |
| Archivos Dart | ~60 |
| Plataformas soportadas | 3 (Android, iOS, Web) |
| Roles de usuario | 3 (cliente, conductor, administrador) |
| Tipos de servicio | 6 (batería, llanta, gasolina, grúa, mecánico, cerrajero) |
| Estados de solicitud | 8 (pending → arrived → service_started → completed) |
| Dependencias de producción | 13 |
8. Lecciones Aprendidas
Una sola base de código para tres apps. Usar Flutter permitió desarrollar las experiencias de cliente, conductor y administrador compartiendo modelos, servicios y utilidades. El tiempo de desarrollo se redujo significativamente frente a construir tres apps nativas separadas.
Supabase como backend completo. Auth + base de datos + tiempo real en un solo servicio eliminó la necesidad de un backend custom. Las Edge Functions cubren los pocos casos que requieren lógica server-side (envío de push notifications).
Abstraer el proveedor de mapas desde el inicio. La rama
mapboxversiondemostró que abstraer la capa de mapas es posible incluso en un proyecto maduro, pero hacerlo desde el diseño inicial habría simplificado la migración.El sistema de cola de despacho fue lo más complejo. Gestionar invitaciones, rechazos, timeouts, re-despacho y concurrencia entre múltiples conductores requirió un modelado cuidadoso de estados y manejo de condiciones de carrera en las suscripciones de tiempo real.
Los fallbacks importan. Los tres niveles de fallback en el cálculo de rutas garantizan que la app nunca se quede sin mostrar ETA, incluso en zonas sin cobertura de datos o cuando la API de Google tiene incidencias.
Si tienes dudas o consultas de como se resolvieron algunos problemas o quieres discutir algun tema en concreto pongo en los comentarios y lo platicamos. 😁
Comentarios (0)