← RETURN_TO_DB
CLASSIFICATION_LEVELPUBLIC
REF_ID: 9E663
DATE06.2026

Caso de estudio: SGU-ASE Aplicativo para servicios viales para aseguradoras.

Case study data record.

Tech_Stack / Tags

MOBILEWEBUSERSFLUTTERGCPPOSTGIS

Meta

  • Creado: Jun 29, 2026, 6:48:51 PM
  • Actualizado: Jun 30, 2026, 7:54:28 AM

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:

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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 — extiende auth.users con 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:

  1. 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.
  2. Despacho automático. El sistema busca conductores disponibles en un radio de 15 km usando consultas espaciales PostGIS y les envía la solicitud.
  3. 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).
  4. 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:

  1. 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.
  2. Aceptación/rechazo. El conductor puede aceptar o rechazar. Si rechaza, el sistema automáticamente busca al siguiente conductor disponible en la cola.
  3. 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.
  4. 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.
  5. 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:

  1. Google Directions API — cálculo preciso con tráfico real, polilínea de ruta
  2. OSRM (Open Source Routing Machine) — API pública gratuita como respaldo
  3. 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 proveedores
  • lib/utils/google_maps_compat.dart (76 líneas) — capa de compatibilidad
  • lib/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 AuthProvider implementa 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 Selector en 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

  1. 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.

  2. 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).

  3. Abstraer el proveedor de mapas desde el inicio. La rama mapboxversion demostró 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.

  4. 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.

  5. 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. 😁

Escrito por@ARBE

Comentarios (0)