Co-authored-by: Azareth-Tr <Azareth-Tr@users.noreply.github.com>
Co-authored-by: MENDOZA BALLARDO GAEL RICARDO <gael-meb123@users.noreply.github.com> configuracion para firebase
This commit is contained in:
366
claude (1).md
Normal file
366
claude (1).md
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
# Contexto del Proyecto — Sistema de Recolección Inteligente y Privada de Residuos
|
||||||
|
|
||||||
|
> **Documento para compartir con asistentes de IA (Claude, ChatGPT, etc.)**
|
||||||
|
> Última actualización: Mayo 2026
|
||||||
|
> Equipo: 4 personas, hackathon, stack principal Flutter + FastAPI
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. ¿Qué estamos construyendo?
|
||||||
|
|
||||||
|
Una **app móvil (Flutter)** que notifica a los ciudadanos cuándo llegará el camión recolector a su domicilio **sin revelar la ubicación del camión en tiempo real ni mostrar el mapa completo de la ruta**. Incluye educación sobre separación de residuos mediante una mascota de IA y gestión de cambios operativos (fallas mecánicas, reasignaciones de rutas). Tres roles: ciudadano, chofer, admin.
|
||||||
|
|
||||||
|
### El problema que resuelve
|
||||||
|
La gente no sabe cuándo pasará el camión → saca la basura demasiado temprano/tarde → problemas de salud pública. La solución obvia (GPS en vivo del camión) es un riesgo de seguridad y uso inadecuado. Nuestra app balancea información útil con privacidad operativa.
|
||||||
|
|
||||||
|
### Alcance del MVP
|
||||||
|
- **7 colonias** de Celaya, Gto. mapeadas a **6 rutas** (datos provistos en JSON).
|
||||||
|
- Notificaciones push en 3 momentos: ruta inicia, camión cerca (~15 min), servicio finalizado.
|
||||||
|
- Validación de domicilio con OCR de recibo (luz/agua) que se borra tras extraer la dirección.
|
||||||
|
- Mascota IA interactiva + guía offline de separación de residuos.
|
||||||
|
- Feedback ciudadano + quiz post-recolección.
|
||||||
|
- Panel admin con mapa (única vista que ve coordenadas) y gestión de reasignaciones.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Reglas innegociables (Privacidad por Diseño)
|
||||||
|
|
||||||
|
Estas son las **prohibiciones del reto** convertidas en requisitos de código. Cualquier feature que las viole se rechaza:
|
||||||
|
|
||||||
|
1. **NUNCA se devuelven coordenadas del camión al ciudadano.** Solo texto: `"Llega en ~15 min"` / `"7:20–7:35 p.m."`.
|
||||||
|
2. **NUNCA hay un mapa con el camión moviéndose** en la vista ciudadana (prohibido el *route tracker*).
|
||||||
|
3. **Visión de túnel:** el ciudadano solo ve su ruta asignada. Cero visibilidad de colonias vecinas u otros usuarios (anti-*snooping*).
|
||||||
|
4. La ubicación del camión **solo existe en el panel admin** (para troubleshooting).
|
||||||
|
5. **Mensajería preventiva:** textos que desalientan sacar basura fuera de horario o perseguir la unidad.
|
||||||
|
6. Quejas hacia la unidad **no exponen** la identidad del chofer (solo "unidad 101"; el admin sí ve quién la conduce).
|
||||||
|
7. Los recibos (validación de domicilio) **no se almacenan crudos**: se procesan, se valida, se guarda solo `verified=true` y se **borra** la imagen.
|
||||||
|
|
||||||
|
**Implementación técnica del túnel:**
|
||||||
|
- Backend: Row Level Security (RLS) de Supabase — el ciudadano físicamente no puede leer `route_positions` ni domicilios ajenos aunque evada la API.
|
||||||
|
- Frontend: notificaciones FCM por **topic de ruta** (`topic_RUTA-01`) — cada ciudadano se suscribe solo al topic de su `routeId`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Stack tecnológico (y por qué)
|
||||||
|
|
||||||
|
| Capa | Tecnología | Razón |
|
||||||
|
|------|------------|-------|
|
||||||
|
| **Frontend móvil** | Flutter + Riverpod (estado) + `dio` (HTTP) + `go_router` | Multiplataforma iOS/Android con un solo código. Riverpod evita polling excesivo. |
|
||||||
|
| **Backend / API** | Python + FastAPI | REST async, rápida de escribir, separa lógica de negocio de datos. |
|
||||||
|
| **Base de datos** | PostgreSQL (vía Supabase); PostGIS opcional | El mapeo colonia→ruta es directo (JSON), así que no necesitamos point-in-polygon. PostGIS solo si quieren validar por polígono en el futuro. |
|
||||||
|
| **Auth + RBAC** | Supabase Auth (JWT) + Row Level Security (RLS) | RLS aplica el túnel **a nivel de BD**, no solo en código — aunque alguien evada la API, la BD niega el acceso. |
|
||||||
|
| **Almacenamiento** | Supabase Storage | Para subir recibo temporalmente. Se borra tras validación. |
|
||||||
|
| **Simulación** | APScheduler (FastAPI cron job) | Avanza `positionId` de cada ruta (1→8) y dispara notificaciones. No hace falta cálculo geoespacial. |
|
||||||
|
| **Push** | Firebase Cloud Messaging (FCM) | Asíncrono, ligero, integración nativa con Flutter. |
|
||||||
|
| **Mascota IA** | API de Claude/Gemini con system prompt sobre separación | El chat educativo. Guía estática (JSON local) funciona offline. |
|
||||||
|
| **OCR validación** | Tesseract o modelo de visión en backend | Extrae dirección del recibo, compara, descarta imagen. |
|
||||||
|
| **Deploy** | Cloud Run / Render (backend scale-to-zero) + Supabase | Escala a cero en valles de inactividad. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Datos provistos (la base real del proyecto)
|
||||||
|
|
||||||
|
Nos entregaron 3 archivos JSON que **definen el modelo y simplifican la implementación**:
|
||||||
|
|
||||||
|
### `rutas.json` (15 rutas, usamos 6)
|
||||||
|
- Cada ruta tiene un `routeId` (ej. `"RUTA-01"`), `truckId` (101–115), y **8 posiciones fijas** (`positionId` 1→8) con lat/lng, velocidad y timestamp.
|
||||||
|
- Todas salen y regresan al **Relleno Sanitario** (`20.5111, -100.9037`) — posición 1 = posición 8.
|
||||||
|
- Velocidad = 0 en posición 5 (punto más lejano / vuelta).
|
||||||
|
- Estas coordenadas **solo se usan en el panel admin** — nunca se mandan al ciudadano.
|
||||||
|
|
||||||
|
### `notificaciones.json` (motor de eventos)
|
||||||
|
Define los 3 eventos que **NO se calculan por distancia**, sino por **cambio de `positionId`**:
|
||||||
|
|
||||||
|
| Evento | Se dispara cuando | Mensaje (resumen) |
|
||||||
|
|--------|-------------------|-------------------|
|
||||||
|
| `ROUTE_START` | `positionId` pasa de 1 → 2 | Salió del relleno rumbo a tu sector |
|
||||||
|
| `TRUCK_PROXIMITY` | `positionId` llega a 4 | A menos de 15 min; saca tus bolsas |
|
||||||
|
| `ROUTE_COMPLETED` | `positionId` llega a 8 | Servicio del día finalizado |
|
||||||
|
|
||||||
|
### `colonias-rutas.json` (el puente colonia → ruta)
|
||||||
|
Mapea cada colonia a un `routeId` + horario. **Esto es el "túnel":** ciudadano → colonia → `routeId` → solo eventos de ESA ruta.
|
||||||
|
|
||||||
|
**Alcance del MVP (7 colonias / 6 rutas):**
|
||||||
|
|
||||||
|
| Colonia | routeId | Turno |
|
||||||
|
|---------|---------|-------|
|
||||||
|
| Zona Centro | RUTA-01 | Matutino |
|
||||||
|
| Las Arboledas | RUTA-01 | Matutino |
|
||||||
|
| San Juanico | RUTA-03 | Matutino |
|
||||||
|
| Los Olivos | RUTA-04 | Matutino |
|
||||||
|
| **Rancho Seco** | **RUTA-05** | **Vespertino** ← única vespertina; úsenla para demostrar reasignación |
|
||||||
|
| Las Insurgentes | RUTA-12 | Matutino |
|
||||||
|
| Trojes | RUTA-13 | Matutino |
|
||||||
|
|
||||||
|
En el registro, el ciudadano **elige su colonia de un dropdown** (no geocodificación). Eso le asigna su `routeId` y resuelve el túnel sin PostGIS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Modelo de datos (jerarquía: Usuario → Domicilio → Colonia → Ruta)
|
||||||
|
|
||||||
|
```sql
|
||||||
|
users (id, email/phone, role: 'citizen'|'driver'|'admin')
|
||||||
|
addresses (id, user_id, label, calle, colonia, route_id,
|
||||||
|
verified BOOL, verified_method, verified_at)
|
||||||
|
colonias (id, nombre, route_id, horario_estimado, turno)
|
||||||
|
routes (id 'RUTA-01'..'RUTA-13', name, truck_id,
|
||||||
|
turno: 'matutino'|'vespertino',
|
||||||
|
status: 'pendiente'|'en_ruta'|'completada'|'diferida'|'reasignada',
|
||||||
|
current_position_id INT) -- el "avance" simulado vive aquí (1-8)
|
||||||
|
route_positions (route_id, position_id, lat, lng, speed, ts)
|
||||||
|
-- de rutas.json; ⚠️ SOLO panel admin (RLS)
|
||||||
|
units (id 101.., plate, status) -- 1:1 con driver y con route
|
||||||
|
drivers (id, user_id, unit_id) -- cada chofer = 1 unidad
|
||||||
|
collection_events(id, route_id, address_id, status, collected_at)
|
||||||
|
feedback (id, user_id, address_id, type, target_unit_id,
|
||||||
|
message, rating, created_at) -- target = unidad, NO chofer
|
||||||
|
notifications (id, user_id, route_id, type, payload, sent_at)
|
||||||
|
route_changes (id, route_id, from_unit, to_unit, reason, created_at)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Cambio clave:** el estado del camión NO es una coordenada calculada en vivo, sino el campo `current_position_id` (1–8). Las coordenadas de `route_positions` solo las consume el panel admin.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Cómo funciona la simulación (el corazón del sistema)
|
||||||
|
|
||||||
|
La simulación NO calcula distancias ni ETA por geolocalización. Es un **índice que avanza** + disparadores de eventos.
|
||||||
|
|
||||||
|
### El cron job (APScheduler en FastAPI)
|
||||||
|
```python
|
||||||
|
# El "estado" de cada ruta es solo en qué positionId va (1-8).
|
||||||
|
estado = { "RUTA-01": 1, "RUTA-03": 1, "RUTA-04": 1,
|
||||||
|
"RUTA-05": 1, "RUTA-12": 1, "RUTA-13": 1 }
|
||||||
|
|
||||||
|
def tick(): # corre cada N segundos (APScheduler)
|
||||||
|
for route_id, pos in estado.items():
|
||||||
|
if pos < 8:
|
||||||
|
nueva = pos + 1
|
||||||
|
estado[route_id] = nueva # = routes.current_position_id en BD
|
||||||
|
disparar_si_aplica(route_id, pos, nueva)
|
||||||
|
|
||||||
|
def disparar_si_aplica(route_id, antes, ahora):
|
||||||
|
# Lee notificaciones.json y dispara según el cambio de positionId
|
||||||
|
if antes == 1 and ahora == 2: push(route_id, "ROUTE_START")
|
||||||
|
elif ahora == 4: push(route_id, "TRUCK_PROXIMITY")
|
||||||
|
elif ahora == 8: push(route_id, "ROUTE_COMPLETED")
|
||||||
|
```
|
||||||
|
|
||||||
|
### El túnel vía FCM topics
|
||||||
|
```python
|
||||||
|
# La push solo va a quienes están en esa ruta (no broadcast).
|
||||||
|
def push(route_id, evento):
|
||||||
|
payload = NOTIFICACIONES[evento]["pushPayload"] # de notificaciones.json
|
||||||
|
fcm.send_to_topic(f"topic_{route_id}", payload) # ej. "topic_RUTA-01"
|
||||||
|
# El ciudadano se suscribe SOLO al topic de su routeId → nunca recibe de otra ruta.
|
||||||
|
# El payload solo lleva texto (title/body); JAMÁS lat/lng.
|
||||||
|
```
|
||||||
|
|
||||||
|
### ETA sin coordenadas (endpoint ciudadano)
|
||||||
|
```python
|
||||||
|
# GET /eta?address_id=123 (ciudadano)
|
||||||
|
route = get_route_for_address(address_id) # derivado de colonia
|
||||||
|
pos = route.current_position_id
|
||||||
|
|
||||||
|
if pos < 4: mensaje = "El camión va en camino a tu sector"
|
||||||
|
elif pos == 4: mensaje = "Llega en aproximadamente 15 minutos"
|
||||||
|
elif pos < 8: mensaje = "Está atendiendo tu zona; saca tus bolsas"
|
||||||
|
else: mensaje = "Servicio del día finalizado"
|
||||||
|
|
||||||
|
# Devuelve SOLO texto:
|
||||||
|
return {"mensaje": mensaje, "status": route.status}
|
||||||
|
# NUNCA coordenadas.
|
||||||
|
```
|
||||||
|
|
||||||
|
### RLS (el túnel a nivel de BD)
|
||||||
|
```sql
|
||||||
|
-- El ciudadano SOLO ve sus domicilios (refuerzo del túnel):
|
||||||
|
CREATE POLICY citizen_own_addresses ON addresses
|
||||||
|
FOR SELECT USING (auth.uid() = user_id);
|
||||||
|
|
||||||
|
-- route_positions (coordenadas): sin policy para citizen = sin acceso. Solo admin:
|
||||||
|
CREATE POLICY admin_route_positions ON route_positions
|
||||||
|
FOR SELECT USING (
|
||||||
|
(SELECT role FROM users WHERE id = auth.uid()) = 'admin'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Lógica de turnos y reasignación
|
||||||
|
|
||||||
|
De las notas del equipo:
|
||||||
|
- Una ruta es **matutina O vespertina**, nunca ambas.
|
||||||
|
- Ruta **matutina** que no se completó → se **difiere** al día siguiente + **notifica**.
|
||||||
|
- Ruta **vespertina** que la unidad no puede atender → la **reemplaza una unidad de mañana** (ya libre) + **notifica**. (Rancho Seco / RUTA-05 es el caso de demo.)
|
||||||
|
- Cada cambio escribe en `route_changes` y dispara FCM con evento `reasignacion`/`retraso` al topic de la ruta.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Reparto del equipo (4 personas)
|
||||||
|
|
||||||
|
| Persona | Bloque | Responsabilidad |
|
||||||
|
|---------|--------|-----------------|
|
||||||
|
| **P1 (Líder)** | Setup + Backend & Seguridad | Inicializar Flutter/backend, arquitectura API REST, JWT, RBAC/RLS, schema, validación de domicilio (OCR + privacidad). |
|
||||||
|
| **P2** | Simulación + Notificaciones + Datos | Carga de los 3 JSON, simulación por `positionId` (cron), motor de eventos/FCM, lógica de turnos/reasignación, endpoint de ETA por texto. |
|
||||||
|
| **P3** | Frontend Ciudadano (Flutter) | Registro, alta/validación de domicilios (dropdown colonia), vista ETA, suscripción FCM al topic de su ruta, buzón de retroalimentación, quiz de satisfacción. |
|
||||||
|
| **P4** | Frontend Admin + Chofer + Mascota IA | Paneles admin (mapa de posiciones, única vista con coordenadas) y chofer (marcar avance, reportar falla), mascota interactiva (LLM), guía de separación offline (JSON local). |
|
||||||
|
|
||||||
|
**Pair programming:** VS Code Live Share. Parejas sugeridas: P1↔P2 (backend/datos) y P3↔P4 (Flutter).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Estructura de archivos del monorepo
|
||||||
|
|
||||||
|
```
|
||||||
|
ONLINESHACK/ <- raíz del repo (git aquí)
|
||||||
|
├── .gitignore <- raíz (cubre Flutter + Python)
|
||||||
|
├── README.md
|
||||||
|
├── PLAN.md <- el checklist del proyecto
|
||||||
|
├── claude.md <- este archivo
|
||||||
|
├── recolecta_app/ <- Flutter
|
||||||
|
│ ├── lib/
|
||||||
|
│ │ ├── core/ (theme, network/dio_client, auth, constants)
|
||||||
|
│ │ ├── features/ (auth, addresses, eta, notifications,
|
||||||
|
│ │ │ separation_guide, feedback, admin, driver)
|
||||||
|
│ │ └── shared/ (widgets reutilizables)
|
||||||
|
│ ├── assets/
|
||||||
|
│ │ └── .env <- (opcional) config pública: API_BASE_URL, anon key
|
||||||
|
│ ├── pubspec.yaml
|
||||||
|
│ └── ...
|
||||||
|
└── backend/ <- FastAPI
|
||||||
|
├── app/
|
||||||
|
│ ├── api/ (routers: auth, addresses, routes, eta, feedback, admin)
|
||||||
|
│ ├── core/ (config, security/jwt, deps)
|
||||||
|
│ ├── schemas/ (DTOs Pydantic)
|
||||||
|
│ ├── services/ (simulation, notifications, reassignment, ocr)
|
||||||
|
│ ├── data/ <- los 3 JSON provistos
|
||||||
|
│ │ ├── colonias-rutas.json
|
||||||
|
│ │ ├── notificaciones.json
|
||||||
|
│ │ └── rutas.json
|
||||||
|
│ └── db/ (session, seed.py)
|
||||||
|
├── secrets/
|
||||||
|
│ └── firebase-adminsdk.json <- ignorado
|
||||||
|
├── .env <- secretos reales (ignorado)
|
||||||
|
├── .env.example <- plantilla (versionada)
|
||||||
|
├── requirements.txt
|
||||||
|
└── main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Secretos y `.env`
|
||||||
|
|
||||||
|
**Backend `.env` (secretos reales, nunca al cliente):**
|
||||||
|
```bash
|
||||||
|
SUPABASE_URL=https://xxx.supabase.co
|
||||||
|
SUPABASE_ANON_KEY=eyJ... # llave pública; puede ir al cliente
|
||||||
|
SUPABASE_SERVICE_ROLE_KEY=eyJ... # llave secreta; SOLO backend
|
||||||
|
JWT_SECRET=xxx
|
||||||
|
FIREBASE_CREDENTIALS_PATH=./secrets/firebase-adminsdk.json
|
||||||
|
SIMULATION_TICK_SECONDS=10
|
||||||
|
```
|
||||||
|
|
||||||
|
**Flutter `.env` (SOLO config pública, viaja en el .apk/.ipa):**
|
||||||
|
```bash
|
||||||
|
API_BASE_URL=http://10.0.2.2:8000 # 10.0.2.2 = localhost desde emulador Android
|
||||||
|
SUPABASE_URL=https://xxx.supabase.co
|
||||||
|
SUPABASE_ANON_KEY=eyJ... # llave anon: pública por diseño
|
||||||
|
```
|
||||||
|
|
||||||
|
**Principio:** cualquier cosa que meta en Flutter es extraíble → no es secreto. Los secretos reales (service_role, admin SDK) viven únicamente en el backend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11. Decisiones arquitectónicas clave (y por qué)
|
||||||
|
|
||||||
|
1. **Supabase RLS en vez de RBAC solo en código:** aunque alguien evada la API (bug, exploit), la BD niega el acceso. Es "defense in depth" para Privacidad por Diseño.
|
||||||
|
|
||||||
|
2. **Simulación por `positionId` en vez de cálculo geoespacial:** los datos provistos ya definen 8 posiciones fijas con timestamps. Avanzar un índice es trivial, y los eventos salen de `notificaciones.json` sin cálculos. Evita complejidad innecesaria.
|
||||||
|
|
||||||
|
3. **FCM por topic de ruta en vez de push individual:** escala mejor (un mensaje llega a todos los ciudadanos de la ruta). El túnel se resuelve en la suscripción: cada quien solo está en `topic_{su_routeId}`.
|
||||||
|
|
||||||
|
4. **Dropdown de colonia en vez de geocodificación:** los datos ya mapean colonia→ruta. Geocodificar sería redundante y costaría llamadas a API. El dropdown es más rápido, más barato y suficiente para el MVP.
|
||||||
|
|
||||||
|
5. **OCR + borrado del recibo:** cumple "Privacidad por Diseño" literalmente — la imagen nunca persiste. Solo queda `verified=true` + método + timestamp.
|
||||||
|
|
||||||
|
6. **Guía offline + chat IA:** la guía estática (JSON embebido en Flutter) garantiza que funcione sin red. El chat con LLM es la capa extra cuando hay conexión.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Endpoints clave del backend
|
||||||
|
|
||||||
|
### Ciudadano (autenticado, rol `citizen`)
|
||||||
|
- `POST /auth/register` — registro con email/teléfono
|
||||||
|
- `POST /auth/login` — devuelve JWT
|
||||||
|
- `GET /colonias` — lista para el dropdown (nombre + horario)
|
||||||
|
- `POST /addresses` — dar de alta domicilio (calle + colonia elegida → deriva `route_id`)
|
||||||
|
- `POST /addresses/{id}/verify` — sube recibo, OCR extrae dirección, compara, borra imagen, guarda `verified=true`
|
||||||
|
- `GET /eta?address_id=X` — devuelve `{mensaje, status}` SIN coordenadas
|
||||||
|
- `POST /feedback` — queja/rating hacia la unidad (NO chofer)
|
||||||
|
|
||||||
|
### Chofer (rol `driver`)
|
||||||
|
- `GET /routes/mine` — su ruta/unidad asignada
|
||||||
|
- `POST /collections/{route_id}` — marcar recolección en un domicilio
|
||||||
|
- `POST /incidents` — reportar falla mecánica (dispara reasignación)
|
||||||
|
|
||||||
|
### Admin (rol `admin`)
|
||||||
|
- `GET /routes` — todas las rutas + `current_position_id`
|
||||||
|
- `GET /routes/{id}/positions` — las 8 coordenadas de `route_positions` (⚠️ única vista con lat/lng)
|
||||||
|
- `POST /routes/{id}/reassign` — reasignar ruta a otra unidad + dispara FCM
|
||||||
|
- `GET /drivers` — lista de choferes + su unidad
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13. Checklist de validación de privacidad (antes de cada demo)
|
||||||
|
|
||||||
|
Antes de mostrar cualquier feature, revisar:
|
||||||
|
- [ ] ¿El endpoint del ciudadano devuelve coordenadas? → ❌ rechazado
|
||||||
|
- [ ] ¿Hay un mapa con el camión moviéndose en vista ciudadana? → ❌ rechazado
|
||||||
|
- [ ] ¿El ciudadano puede pedir datos de otra ruta u otro usuario? → ❌ debe fallar (RLS)
|
||||||
|
- [ ] ¿Las quejas exponen el nombre/ID del chofer? → ❌ solo `target_unit_id`
|
||||||
|
- [ ] ¿El recibo del ciudadano quedó almacenado en BD o storage? → ❌ debe estar borrado
|
||||||
|
- [ ] ¿Los mensajes desalientan sacar basura fuera de horario? → ✅ validar textos
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 14. Comandos útiles de arranque
|
||||||
|
|
||||||
|
### Flutter
|
||||||
|
```bash
|
||||||
|
cd recolecta_app
|
||||||
|
flutter pub get
|
||||||
|
flutter run # Android: conectar emulador o dispositivo
|
||||||
|
```
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
```bash
|
||||||
|
cd backend
|
||||||
|
python -m venv .venv && source .venv/bin/activate # (Windows: .venv\Scripts\activate)
|
||||||
|
pip install -r requirements.txt
|
||||||
|
# Crear .env copiando .env.example y llenando valores
|
||||||
|
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
|
||||||
|
```
|
||||||
|
|
||||||
|
### Simulación (una vez el backend esté corriendo)
|
||||||
|
El cron de APScheduler arranca automáticamente con el backend si está configurado. Para forzar un tick manual desde consola Python:
|
||||||
|
```python
|
||||||
|
from app.services.simulation import tick
|
||||||
|
tick() # avanza current_position_id de cada ruta y dispara eventos
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 15. Para la demo (guion de 5–7 min)
|
||||||
|
|
||||||
|
1. **Registro + validación de domicilio** (mostrar que el recibo se borra).
|
||||||
|
2. **Correr la simulación acelerada** (tick cada 5 segundos): llega `ROUTE_START` → `TRUCK_PROXIMITY` → `ROUTE_COMPLETED`. Resaltar: **sin mapa, sin ubicación**, solo texto.
|
||||||
|
3. **Mascota IA** enseñando a separar residuos + guía offline.
|
||||||
|
4. **Falla de unidad en Rancho Seco (RUTA-05 vespertina)** → reasignación a unidad matutina → notificación al ciudadano.
|
||||||
|
5. **Panel admin** con el mapa de las 8 posiciones (contraste: solo admin ve coordenadas).
|
||||||
|
6. **Quiz post-recolección** + queja a la unidad (sin exponer chofer).
|
||||||
|
7. **Diapositiva de arquitectura** + cómo cada decisión cumple Privacidad por Diseño.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Fin del contexto. Este documento debe ir al repo como `claude.md` para que el equipo lo comparta con sus asistentes de IA.**
|
||||||
276
recolecta_app/lib/app/app.dart
Normal file
276
recolecta_app/lib/app/app.dart
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import '../firebase_options.dart';
|
||||||
|
|
||||||
|
final bootstrapProvider = FutureProvider<void>((ref) async {
|
||||||
|
await dotenv.load(fileName: 'assets/.env');
|
||||||
|
|
||||||
|
// Inicializar Firebase (si hay DefaultFirebaseOptions, úsalas; sino, intenta initializeApp() y espera que haya google-services/Info.plist)
|
||||||
|
final FirebaseOptions? options = DefaultFirebaseOptions.currentPlatform;
|
||||||
|
if (options != null) {
|
||||||
|
await Firebase.initializeApp(options: options);
|
||||||
|
} else {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Registrar handler para mensajes en background
|
||||||
|
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handler top-level requerido por FCM
|
||||||
|
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||||
|
// Asegurar Firebase inicializado en background isolate
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
} catch (_) {
|
||||||
|
// ignore if already initialized
|
||||||
|
}
|
||||||
|
// Aquí puedes procesar y guardar la notificación si hace falta
|
||||||
|
debugPrint(
|
||||||
|
'FCM background message received: ${message.messageId} | data: ${message.data}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final apiClientProvider = Provider<Dio>((ref) {
|
||||||
|
final baseUrl = dotenv.env['API_BASE_URL'] ?? 'http://10.0.2.2:8000';
|
||||||
|
|
||||||
|
return Dio(
|
||||||
|
BaseOptions(
|
||||||
|
baseUrl: baseUrl,
|
||||||
|
connectTimeout: const Duration(seconds: 15),
|
||||||
|
receiveTimeout: const Duration(seconds: 15),
|
||||||
|
headers: const <String, dynamic>{'Content-Type': 'application/json'},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
final secureStorageProvider = Provider<FlutterSecureStorage>((ref) {
|
||||||
|
return const FlutterSecureStorage();
|
||||||
|
});
|
||||||
|
|
||||||
|
final routerProvider = Provider<GoRouter>((ref) {
|
||||||
|
return GoRouter(
|
||||||
|
initialLocation: '/home',
|
||||||
|
routes: <RouteBase>[
|
||||||
|
GoRoute(
|
||||||
|
path: '/home',
|
||||||
|
name: 'home',
|
||||||
|
builder: (context, state) => const HomePage(),
|
||||||
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: '/status',
|
||||||
|
name: 'status',
|
||||||
|
builder: (context, state) => const StatusPage(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
class RecolectaApp extends ConsumerWidget {
|
||||||
|
const RecolectaApp({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final bootstrap = ref.watch(bootstrapProvider);
|
||||||
|
|
||||||
|
return bootstrap.when(
|
||||||
|
loading: () => const MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: BootstrapLoadingPage(),
|
||||||
|
),
|
||||||
|
error: (error, stackTrace) => MaterialApp(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
home: BootstrapErrorPage(error: error),
|
||||||
|
),
|
||||||
|
data: (_) => MaterialApp.router(
|
||||||
|
debugShowCheckedModeBanner: false,
|
||||||
|
title: 'Recolecta',
|
||||||
|
theme: ThemeData(
|
||||||
|
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1F6F78)),
|
||||||
|
scaffoldBackgroundColor: const Color(0xFFF4F7F6),
|
||||||
|
useMaterial3: true,
|
||||||
|
),
|
||||||
|
routerConfig: ref.watch(routerProvider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BootstrapLoadingPage extends StatelessWidget {
|
||||||
|
const BootstrapLoadingPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return const Scaffold(body: Center(child: CircularProgressIndicator()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BootstrapErrorPage extends StatelessWidget {
|
||||||
|
const BootstrapErrorPage({super.key, required this.error});
|
||||||
|
|
||||||
|
final Object error;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(Icons.error_outline, size: 48),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
const Text(
|
||||||
|
'No se pudo cargar la configuración inicial.',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 12),
|
||||||
|
Text(
|
||||||
|
error.toString(),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: Theme.of(context).textTheme.bodySmall,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class HomePage extends ConsumerWidget {
|
||||||
|
const HomePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final dio = ref.read(apiClientProvider);
|
||||||
|
final storage = ref.read(secureStorageProvider);
|
||||||
|
final baseUrl = dio.options.baseUrl;
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Recolecta'),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => context.goNamed('status'),
|
||||||
|
icon: const Icon(Icons.route),
|
||||||
|
tooltip: 'Estado',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Bootstrap listo',
|
||||||
|
style: TextStyle(fontSize: 28, fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Text(
|
||||||
|
'La app ya carga .env, Riverpod y GoRouter para la base del MVP.',
|
||||||
|
style: Theme.of(context).textTheme.bodyLarge,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
_InfoCard(title: 'API base URL', value: baseUrl, icon: Icons.cloud),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_InfoCard(
|
||||||
|
title: 'Secure Storage',
|
||||||
|
value: storage.runtimeType.toString(),
|
||||||
|
icon: Icons.lock,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
_ImageCard(
|
||||||
|
title: 'Widget listo para caché de imágenes',
|
||||||
|
imageUrl:
|
||||||
|
'https://images.unsplash.com/photo-1542838132-92c53300491e?auto=format&fit=crop&w=800&q=80',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class StatusPage extends StatelessWidget {
|
||||||
|
const StatusPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text('Estado')),
|
||||||
|
body: const Padding(
|
||||||
|
padding: EdgeInsets.all(24),
|
||||||
|
child: Text(
|
||||||
|
'Aquí después se conectarán ETA, notificaciones, ruta asignada y métricas de privacidad.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _InfoCard extends StatelessWidget {
|
||||||
|
const _InfoCard({
|
||||||
|
required this.title,
|
||||||
|
required this.value,
|
||||||
|
required this.icon,
|
||||||
|
});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String value;
|
||||||
|
final IconData icon;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
elevation: 0,
|
||||||
|
child: ListTile(
|
||||||
|
leading: Icon(icon),
|
||||||
|
title: Text(title),
|
||||||
|
subtitle: Text(value),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ImageCard extends StatelessWidget {
|
||||||
|
const _ImageCard({required this.title, required this.imageUrl});
|
||||||
|
|
||||||
|
final String title;
|
||||||
|
final String imageUrl;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Card(
|
||||||
|
clipBehavior: Clip.antiAlias,
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
CachedNetworkImage(
|
||||||
|
imageUrl: imageUrl,
|
||||||
|
height: 180,
|
||||||
|
width: double.infinity,
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
placeholder: (context, url) => const SizedBox(
|
||||||
|
height: 180,
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
errorWidget: (context, url, error) => const SizedBox(
|
||||||
|
height: 180,
|
||||||
|
child: Center(child: Icon(Icons.image_not_supported)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(padding: const EdgeInsets.all(16), child: Text(title)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
14
recolecta_app/lib/firebase_options.dart
Normal file
14
recolecta_app/lib/firebase_options.dart
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
||||||
|
|
||||||
|
// Placeholder file for Firebase configuration.
|
||||||
|
// Run `flutterfire configure` to generate a proper `firebase_options.dart` file
|
||||||
|
// and replace the implementation below. For local dev you can also rely on
|
||||||
|
// `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) instead
|
||||||
|
// of providing explicit FirebaseOptions.
|
||||||
|
|
||||||
|
class DefaultFirebaseOptions {
|
||||||
|
static FirebaseOptions? get currentPlatform {
|
||||||
|
// TODO: Replace with generated FirebaseOptions from `flutterfire configure`.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,88 +1,9 @@
|
|||||||
// ============================================================
|
|
||||||
// main.dart — App Recolecta (corregido)
|
|
||||||
// Cambios aplicados:
|
|
||||||
// 1. Agregados imports faltantes (material, riverpod)
|
|
||||||
// 2. Corregido ColorScheme.fromSeed (faltaba el tipo)
|
|
||||||
// 3. Corregido MainAxisAlignment.center (faltaba el tipo)
|
|
||||||
// 4. Agregado WidgetsFlutterBinding.ensureInitialized() antes de dotenv
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
|
||||||
|
import 'app/app.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
// NECESARIO antes de cualquier async en main (inicializa el motor de Flutter)
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
runApp(const ProviderScope(child: RecolectaApp()));
|
||||||
// Carga el .env desde assets/
|
|
||||||
await dotenv.load(fileName: "assets/.env");
|
|
||||||
|
|
||||||
runApp(const ProviderScope(child: MyApp()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
|
||||||
const MyApp({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Recolecta',
|
|
||||||
theme: ThemeData(
|
|
||||||
// ❌ ANTES: colorScheme: .fromSeed(seedColor: Colors.deepPurple),
|
|
||||||
// ✅ AHORA: faltaba el tipo ColorScheme antes del .fromSeed
|
|
||||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
|
|
||||||
useMaterial3: true,
|
|
||||||
),
|
|
||||||
home: const MyHomePage(title: 'Recolecta - Demo'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
|
||||||
const MyHomePage({super.key, required this.title});
|
|
||||||
|
|
||||||
final String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<MyHomePage> createState() => _MyHomePageState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MyHomePageState extends State<MyHomePage> {
|
|
||||||
int _counter = 0;
|
|
||||||
|
|
||||||
void _incrementCounter() {
|
|
||||||
setState(() {
|
|
||||||
_counter++;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
|
||||||
title: Text(widget.title),
|
|
||||||
),
|
|
||||||
body: Center(
|
|
||||||
child: Column(
|
|
||||||
// ❌ ANTES: mainAxisAlignment: .center,
|
|
||||||
// ✅ AHORA: faltaba el tipo MainAxisAlignment antes del .center
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Text('You have pushed the button this many times:'),
|
|
||||||
Text(
|
|
||||||
'$_counter',
|
|
||||||
style: Theme.of(context).textTheme.headlineMedium,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
floatingActionButton: FloatingActionButton(
|
|
||||||
onPressed: _incrementCounter,
|
|
||||||
tooltip: 'Increment',
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,10 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
|
||||||
|
|
||||||
void fl_register_plugins(FlPluginRegistry* registry) {
|
void fl_register_plugins(FlPluginRegistry* registry) {
|
||||||
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
|
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
|
||||||
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,11 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
flutter_secure_storage_linux
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
@@ -5,6 +5,14 @@
|
|||||||
import FlutterMacOS
|
import FlutterMacOS
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
import firebase_core
|
||||||
|
import firebase_messaging
|
||||||
|
import flutter_secure_storage_macos
|
||||||
|
import sqflite_darwin
|
||||||
|
|
||||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||||
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||||
|
FlutterSecureStoragePlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStoragePlugin"))
|
||||||
|
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "99.0.0"
|
version: "99.0.0"
|
||||||
|
_flutterfire_internals:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _flutterfire_internals
|
||||||
|
sha256: ff0a84a2734d9e1089f8aedd5c0af0061b82fb94e95260d943404e0ef2134b11
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.59"
|
||||||
analyzer:
|
analyzer:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -41,6 +49,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.2"
|
version: "2.1.2"
|
||||||
|
cached_network_image:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: cached_network_image
|
||||||
|
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
|
cached_network_image_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_platform_interface
|
||||||
|
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.1.1"
|
||||||
|
cached_network_image_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: cached_network_image_web
|
||||||
|
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.1"
|
||||||
characters:
|
characters:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -65,6 +97,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.2"
|
version: "1.1.2"
|
||||||
|
code_assets:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: code_assets
|
||||||
|
sha256: dad6bf6b9f4f378b0a69edbf42584d336efd1a9ce15deb1ba591cbb1b5ff440f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
collection:
|
collection:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -105,6 +145,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.9"
|
version: "1.0.9"
|
||||||
|
dio:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: dio
|
||||||
|
sha256: aff32c08f92787a557dd5c0145ac91536481831a01b4648136373cddb0e64f8c
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.9.2"
|
||||||
|
dio_web_adapter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dio_web_adapter
|
||||||
|
sha256: "2f9e64323a7c3c7ef69567d5c800424a11f8337b8b228bad02524c9fb3c1f340"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -113,6 +169,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.3"
|
version: "1.3.3"
|
||||||
|
ffi:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: ffi
|
||||||
|
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.0"
|
||||||
file:
|
file:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -121,11 +185,75 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "7.0.1"
|
version: "7.0.1"
|
||||||
|
firebase_core:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_core
|
||||||
|
sha256: "7be63a3f841fc9663342f7f3a011a42aef6a61066943c90b1c434d79d5c995c5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.15.2"
|
||||||
|
firebase_core_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_core_platform_interface
|
||||||
|
sha256: "0ecda14c1bfc9ed8cac303dd0f8d04a320811b479362a9a4efb14fd331a473ce"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "6.0.3"
|
||||||
|
firebase_core_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_core_web
|
||||||
|
sha256: "0ed0dc292e8f9ac50992e2394e9d336a0275b6ae400d64163fdf0a8a8b556c37"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.24.1"
|
||||||
|
firebase_messaging:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_messaging
|
||||||
|
sha256: "60be38574f8b5658e2f22b7e311ff2064bea835c248424a383783464e8e02fcc"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "15.2.10"
|
||||||
|
firebase_messaging_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_messaging_platform_interface
|
||||||
|
sha256: "685e1771b3d1f9c8502771ccc9f91485b376ffe16d553533f335b9183ea99754"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.6.10"
|
||||||
|
firebase_messaging_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_messaging_web
|
||||||
|
sha256: "0d1be17bc89ed3ff5001789c92df678b2e963a51b6fa2bdb467532cc9dbed390"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.10.10"
|
||||||
|
fixnum:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: fixnum
|
||||||
|
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.1"
|
||||||
flutter:
|
flutter:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_cache_manager:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_cache_manager
|
||||||
|
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.1"
|
||||||
flutter_dotenv:
|
flutter_dotenv:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@@ -150,11 +278,64 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.3.1"
|
version: "3.3.1"
|
||||||
|
flutter_secure_storage:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage
|
||||||
|
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.2.4"
|
||||||
|
flutter_secure_storage_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_linux
|
||||||
|
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.3"
|
||||||
|
flutter_secure_storage_macos:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_macos
|
||||||
|
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.3"
|
||||||
|
flutter_secure_storage_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_platform_interface
|
||||||
|
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.2"
|
||||||
|
flutter_secure_storage_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_web
|
||||||
|
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.2.1"
|
||||||
|
flutter_secure_storage_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_secure_storage_windows
|
||||||
|
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.2"
|
||||||
flutter_test:
|
flutter_test:
|
||||||
dependency: "direct dev"
|
dependency: "direct dev"
|
||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_web_plugins:
|
||||||
|
dependency: transitive
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -171,6 +352,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.3"
|
version: "2.1.3"
|
||||||
|
go_router:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: go_router
|
||||||
|
sha256: f02fd7d2a4dc512fec615529824fdd217fecb3a3d3de68360293a551f21634b3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "14.8.1"
|
||||||
|
hooks:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: hooks
|
||||||
|
sha256: a41af4e8fc687cd6d33de9751eb936c8c0204ebe2bcb6c15ecf707504bf47f31
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.0.0"
|
||||||
|
http:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: http
|
||||||
|
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.6.0"
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -195,6 +400,30 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.5"
|
version: "1.0.5"
|
||||||
|
jni:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni
|
||||||
|
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
jni_flutter:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: jni_flutter
|
||||||
|
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
|
js:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: js
|
||||||
|
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.7"
|
||||||
leak_tracker:
|
leak_tracker:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -275,6 +504,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
|
objective_c:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: objective_c
|
||||||
|
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "9.4.1"
|
||||||
|
octo_image:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: octo_image
|
||||||
|
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.0"
|
||||||
package_config:
|
package_config:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -291,6 +536,70 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.9.1"
|
version: "1.9.1"
|
||||||
|
path_provider:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider
|
||||||
|
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.5"
|
||||||
|
path_provider_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_android
|
||||||
|
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.1"
|
||||||
|
path_provider_foundation:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_foundation
|
||||||
|
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.6.0"
|
||||||
|
path_provider_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_linux
|
||||||
|
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.2.1"
|
||||||
|
path_provider_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_platform_interface
|
||||||
|
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.2"
|
||||||
|
path_provider_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: path_provider_windows
|
||||||
|
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.3.0"
|
||||||
|
platform:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: platform
|
||||||
|
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.6"
|
||||||
|
plugin_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: plugin_platform_interface
|
||||||
|
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.1.8"
|
||||||
pool:
|
pool:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -307,6 +616,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
|
record_use:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: record_use
|
||||||
|
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.6.0"
|
||||||
riverpod:
|
riverpod:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -315,6 +632,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.2.1"
|
version: "3.2.1"
|
||||||
|
rxdart:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: rxdart
|
||||||
|
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.28.0"
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -376,6 +701,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.10.2"
|
version: "1.10.2"
|
||||||
|
sqflite:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite
|
||||||
|
sha256: "564cfed0746fe53140c23b70b308e045c3b31f17778f2f326ccb7d804ea0250a"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2+1"
|
||||||
|
sqflite_android:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_android
|
||||||
|
sha256: "881e28efdcc9950fd8e9bb42713dcf1103e62a2e7168f23c9338d82db13dec40"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2+3"
|
||||||
|
sqflite_common:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_common
|
||||||
|
sha256: "1581ffbf7a0e333b380d6a30737d78516b826cb35beb7fb0bf8a3ea0c678b465"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.5.8"
|
||||||
|
sqflite_darwin:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_darwin
|
||||||
|
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.2"
|
||||||
|
sqflite_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: sqflite_platform_interface
|
||||||
|
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.4.0"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -408,6 +773,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.1"
|
version: "1.4.1"
|
||||||
|
synchronized:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: synchronized
|
||||||
|
sha256: "63896c27e81b28f8cb4e69ead0d3e8f03f1d1e5fc531a3e579cabed6a2c7c9e5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.4.0+1"
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -448,6 +821,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.4.0"
|
version: "1.4.0"
|
||||||
|
uuid:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: uuid
|
||||||
|
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.5.3"
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -504,6 +885,22 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.1"
|
version: "1.2.1"
|
||||||
|
win32:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: win32
|
||||||
|
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "5.15.0"
|
||||||
|
xdg_directories:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xdg_directories
|
||||||
|
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.1.0"
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -514,4 +911,4 @@ packages:
|
|||||||
version: "3.1.3"
|
version: "3.1.3"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.11.4 <4.0.0"
|
dart: ">=3.11.4 <4.0.0"
|
||||||
flutter: ">=3.18.0-18.0.pre.54"
|
flutter: ">=3.38.4"
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ dependencies:
|
|||||||
cupertino_icons: ^1.0.8
|
cupertino_icons: ^1.0.8
|
||||||
flutter_dotenv: ^6.0.1
|
flutter_dotenv: ^6.0.1
|
||||||
flutter_riverpod: ^3.3.1
|
flutter_riverpod: ^3.3.1
|
||||||
|
dio: ^5.7.0
|
||||||
|
go_router: ^14.6.2
|
||||||
|
firebase_core: ^3.8.0
|
||||||
|
firebase_messaging: ^15.1.5
|
||||||
|
flutter_secure_storage: ^9.2.4
|
||||||
|
cached_network_image: ^3.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
|
|
||||||
#include "generated_plugin_registrant.h"
|
#include "generated_plugin_registrant.h"
|
||||||
|
|
||||||
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
|
#include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
|
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||||
|
FlutterSecureStorageWindowsPluginRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
|
firebase_core
|
||||||
|
flutter_secure_storage_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
jni
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
Reference in New Issue
Block a user