Files
Base-QR-FlutterApp/README.md
Alejandro Lara 50fe9a9d36 Initial commit: QR Scanner Flutter app
App móvil para validar pases QR contra base-admin-web con login Cognito,
escáner en tiempo real, feedback auditivo/háptico y README completo.
2026-06-09 17:03:06 -06:00

12 KiB

QR Scanner

Aplicación móvil Flutter para validar pases de acceso mediante códigos QR. Se conecta al backend base-admin-web para autenticar operadores con rol escáner y registrar cada lectura en el servidor.


Tabla de contenidos


Características

  • Login con usuario y contraseña contra el API de base-admin-web (Cognito / JWT).
  • Sesión persistente opcional con Recordarme (shared_preferences).
  • Escáner QR en tiempo real con ventana de enfoque y overlay visual.
  • Validación del pase en el servidor con accessToken (Bearer).
  • Retroalimentación auditiva y háptica al detectar, validar o rechazar un código.
  • UI oscura con animaciones en login, home y resultados de escaneo.

Arquitectura

flowchart TB
    subgraph App["App Flutter (qrscanner)"]
        UI["Screens\n(login · home · scanner)"]
        Services["Services\n(auth · pass_code · feedback)"]
        Models["Models\n(login_response · auth_session · pass_code)"]
        Config["ApiConfig"]
    end

    subgraph Device["Dispositivo"]
        Camera["Cámara"]
        Prefs["SharedPreferences"]
        Audio["Audio / Vibración"]
    end

    subgraph Backend["base-admin-web :8080"]
        AuthAPI["POST /api/auth/login"]
        ScanAPI["POST /api/pass-codes/scan"]
        Cognito["AWS Cognito / JWT"]
    end

    UI --> Services
    Services --> Models
    Services --> Config
    Services --> Prefs
    UI --> Camera
    Services --> Audio
    Config --> AuthAPI
    Config --> ScanAPI
    AuthAPI --> Cognito
    ScanAPI --> Cognito

Capas de la app

graph LR
    A[Pantallas] --> B[Servicios]
    B --> C[Modelos]
    B --> D[HTTP / API]
    A --> E[Tema / UI]
    B --> F[Almacenamiento local]
    A --> G[mobile_scanner]
Capa Responsabilidad
Screens UI, navegación y estados visuales
Services Lógica de negocio, HTTP y feedback
Models Parseo JSON y sesión de autenticación
Config URL base del API configurable en compilación

Flujos principales

Login

sequenceDiagram
    actor Usuario
    participant Login as LoginScreen
    participant Auth as AuthService
    participant API as base-admin-web
    participant Home as HomeScreen

    Usuario->>Login: Usuario + contraseña
    Login->>Auth: login()
    Auth->>API: POST /api/auth/login
    API-->>Auth: accessToken, refreshToken, idToken, roles
    Auth->>Auth: Valida ROLE_SCANNER
    alt Credenciales válidas
        Auth-->>Login: LoginResponse success
        Login->>Home: Navegar con AuthSession
    else Error
        API-->>Login: success false / sin token
        Login-->>Usuario: Mensaje de error
    end

Escaneo y validación

sequenceDiagram
    actor Operador
    participant Home as HomeScreen
    participant Scan as ScannerScreen
    participant MS as mobile_scanner
    participant Pass as PassCodeService
    participant API as base-admin-web
    participant FB as FeedbackService

    Operador->>Home: Abrir escáner
    Home->>Scan: AuthSession
    loop Escaneo continuo
        MS->>Scan: onDetect (QR)
        Scan->>FB: Sonido + vibración
        Scan->>Pass: scan(code)
        Pass->>API: POST /api/pass-codes/scan\nAuthorization: Bearer accessToken
        API-->>Pass: success + passCode
        Pass-->>Scan: ScanResponse
        Scan->>FB: success / warning
        Scan-->>Operador: Tarjeta verde (nombre + código)\no amarilla (error)
    end

Estados del escáner

stateDiagram-v2
    [*] --> scanning: Abrir cámara
    scanning --> processing: QR detectado
    processing --> success: API válida
    processing --> warning: API rechaza / error red
    success --> scanning: Escanear otro
    warning --> scanning: Escanear otro
    scanning --> [*]: Volver atrás

Estructura del proyecto

qrscanner/
├── lib/
│   ├── main.dart                 # Punto de entrada
│   ├── config/
│   │   └── api_config.dart       # URL base y endpoints
│   ├── models/
│   │   ├── auth_session.dart     # Sesión y header Authorization
│   │   ├── login_response.dart   # Respuesta de login
│   │   ├── pass_code.dart        # Datos del pase
│   │   └── scan_response.dart    # Respuesta de escaneo
│   ├── screens/
│   │   ├── login_screen.dart     # Pantalla de inicio de sesión
│   │   ├── home_screen.dart      # Pantalla principal
│   │   └── scanner_screen.dart   # Cámara y validación QR
│   ├── services/
│   │   ├── auth_service.dart     # Login y sesión
│   │   ├── pass_code_service.dart# Validación de pases
│   │   └── feedback_service.dart # Audio y vibración
│   └── theme/
│       └── app_theme.dart        # Colores y tema oscuro
├── assets/
│   └── sounds/
│       ├── scan.wav
│       ├── success.wav
│       └── warning.wav
├── android/                      # Configuración Android
├── ios/                          # Configuración iOS
├── test/                         # Pruebas unitarias y widget
└── pubspec.yaml                  # Dependencias

Dependencias

Directas (producción)

Paquete Versión en pubspec.yaml Versión instalada Uso
flutter SDK Framework UI
cupertino_icons ^1.0.8 1.0.9 Iconos iOS
http ^1.2.2 1.6.0 Peticiones REST al backend
mobile_scanner ^7.0.1 7.2.0 Lectura de códigos QR con cámara
shared_preferences ^2.3.3 2.5.5 Persistir sesión (Recordarme)
audioplayers ^6.5.0 6.7.1 Sonidos de feedback
vibration ^3.1.3 3.1.8 Vibración al escanear

Desarrollo

Paquete Versión Uso
flutter_test SDK Pruebas widget
flutter_lints ^6.0.0 (6.0.0) Reglas de análisis estático

Dependencias transitivas relevantes

Incluidas automáticamente por los paquetes anteriores:

  • path_provider — rutas de almacenamiento (audioplayers, shared_preferences)
  • device_info_plus — información del dispositivo (vibration)
  • camera / plugins nativos — soporte de cámara (mobile_scanner)

Para verificar versiones actualizadas:

flutter pub outdated

Requisitos

  • Flutter 3.12+ (Dart SDK ^3.12.1)
  • Backend base-admin-web corriendo y accesible en red
  • Dispositivo físico o emulador con cámara (recomendado dispositivo real para QR)
  • Usuario con rol ROLE_SCANNER en Cognito / base-admin-web

Configuración e instalación

1. Clonar e instalar dependencias

cd flutter-app/qrscanner
flutter pub get

2. URL del API

Por defecto la app apunta a:

http://10.99.51.6:8080

Para usar otra IP o puerto:

flutter run --dart-define=API_BASE_URL=http://192.168.1.100:8080

Compilación release con URL personalizada:

flutter build apk --dart-define=API_BASE_URL=http://10.99.51.6:8080

3. Ejecutar en dispositivo

# Listar dispositivos
flutter devices

# Ejecutar
flutter run

# Ejecutar con URL custom
flutter run --dart-define=API_BASE_URL=http://10.99.51.6:8080

4. Credenciales de prueba

Según el entorno de desarrollo de base-admin-web:

Campo Valor
Usuario scanner
Contraseña Scanner123*

El usuario debe pertenecer al grupo scanners / rol ROLE_SCANNER.


API del backend

Base URL configurable: ApiConfig.baseUrl

Login

POST /api/auth/login
Content-Type: application/json

Request:

{
  "username": "scanner",
  "password": "Scanner123*"
}

Response (200):

{
  "success": true,
  "accessToken": "eyJ...",
  "refreshToken": "dXMt...",
  "idToken": "eyJ...",
  "tokenType": "Bearer",
  "expiresIn": 3600,
  "username": "scanner",
  "roles": ["ROLE_SCANNER"]
}

La app usa accessToken en todas las peticiones autenticadas.

Escanear pase

POST /api/pass-codes/scan
Content-Type: application/json
Authorization: Bearer <accessToken>

Request:

{
  "code": "ABC123XYZ"
}

Response exitosa (200):

{
  "success": true,
  "message": "Codigo pase escaneado correctamente.",
  "passCode": {
    "id": 1,
    "name": "Pase visitante",
    "code": "ABC123XYZ",
    "status": "ACTIVE",
    "scanned": true,
    "createdAt": "2026-06-09T10:00:00",
    "scannedAt": "2026-06-09T15:30:00"
  }
}

Response error (400):

{
  "success": false,
  "message": "Descripción del error"
}

Formato del QR

La app acepta:

  • Texto plano con el código del pase
  • URL con el código en query: ?code=ABC123
  • URL con el código como último segmento del path

Permisos

Android (AndroidManifest.xml)

Permiso Motivo
INTERNET Comunicación con el API
CAMERA Escaneo QR
VIBRATE Feedback háptico

android:usesCleartextTraffic="true" habilitado para HTTP en red local (desarrollo).

iOS

Requiere descripción de uso de cámara en Info.plist al desplegar en producción.


Pruebas

# Todas las pruebas
flutter test

# Análisis estático
flutter analyze

Pruebas incluidas:

  • test/widget_test.dart — renderizado de la pantalla de login
  • test/login_response_test.dart — parseo de respuesta Cognito y uso de accessToken

Compilación

Android APK

flutter build apk --release \
  --dart-define=API_BASE_URL=http://10.99.51.6:8080

Salida: build/app/outputs/flutter-apk/app-release.apk

Android App Bundle (Play Store)

flutter build appbundle --release \
  --dart-define=API_BASE_URL=https://tu-servidor.com

iOS

flutter build ios --release \
  --dart-define=API_BASE_URL=https://tu-servidor.com

Diagrama de integración con base-admin-web

flowchart LR
    subgraph Mobile["📱 QR Scanner"]
        A[Login]
        B[Escáner]
    end

    subgraph Server["🖥️ base-admin-web"]
        C["/api/auth/login"]
        D["/api/pass-codes/scan"]
        E[(Base de datos)]
    end

    subgraph Auth["🔐 Cognito"]
        F[JWT / Tokens]
    end

    A -->|username + password| C
    C --> F
    F -->|accessToken| A
    B -->|Bearer accessToken + code| D
    D --> E
    D -->|passCode| B

Notas técnicas

  • Token activo: solo se envía accessToken en el header Authorization.
  • Sesión: si "Recordarme" está activo, se guardan accessToken, refreshToken, idToken, username y roles en SharedPreferences.
  • Escáner: modo DetectionSpeed.normal, solo formato QR, con autoZoom en Android.
  • Resultado válido: la tarjeta verde muestra únicamente nombre y código del pase.