Añadir 02-frontend.md

2026-05-23 02:51:55 +00:00
parent e92e0746c1
commit b6c4c5ed42

442
02-frontend.md.-.md Normal file

@@ -0,0 +1,442 @@
# Configurar Flutter (Módulos C + D)
Guía paso a paso para integrar los módulos y correr la app.
---
## 1. Clonar el repo o descargar módulos
### Si ya está en el repo:
```bash
cd hackathon-acapulquitos-boys
git pull origin dev
cd basura_app
```
### Si Persona D entrega su módulo por separado:
```bash
# Descomprimir ZIP del módulo D
unzip persona_d_modulo.zip
# Copiar archivos al proyecto principal
cp -r persona_d/lib/core basura_app/lib/
cp -r persona_d/lib/features/recycling_guide basura_app/lib/features/
cp persona_d/assets/recycling_guide.json basura_app/assets/
```
---
## 2. Configurar FVM (recomendado)
```bash
cd basura_app
# Instalar la versión acordada por el equipo
fvm install 3.44.0 # o 3.22.0 si bajaron versión
fvm use 3.44.0
# Verificar
fvm flutter --version
```
Esto crea `.fvm/fvm_config.json` — debe commitearse al repo.
---
## 3. Actualizar `pubspec.yaml`
Abre `basura_app/pubspec.yaml` y verifica:
```yaml
name: basura_app
description: App de notificación de recolección de residuos
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=3.8.0 <4.0.0' # Dart 3.8 (Flutter 3.44)
flutter: '>=3.44.0' # o '>=3.22.0' si bajaron
dependencies:
flutter:
sdk: flutter
# State management
flutter_riverpod: ^2.6.1
# HTTP
dio: ^5.4.0
# WebSocket
web_socket_channel: ^2.4.0
# Routing (Persona C decide cuál usar)
go_router: ^13.2.0 # Recomendado
# o usa Navigator tradicional
# Utils
intl: ^0.19.0 # Formateo de fechas
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^4.0.0
flutter:
uses-material-design: true
assets:
- assets/recycling_guide.json # ← JSON del módulo D
```
---
## 4. Instalar dependencias
```bash
fvm flutter pub get
```
Deberías ver:
```
Running "flutter pub get" in basura_app...
Resolving dependencies...
Got dependencies!
```
---
## 5. Verificar la estructura de carpetas
```bash
tree -L 4 lib/
```
Debe verse algo así:
```
lib/
├── core/
│ └── theme/
│ └── app_theme.dart # Tema compartido (Persona D)
├── features/
│ ├── auth/ # Login (Persona C)
│ │ └── presentation/
│ │ └── screens/
│ │ └── login_screen.dart
│ │
│ ├── eta/ # Home + WebSocket (Persona C)
│ │ ├── domain/
│ │ ├── data/
│ │ └── presentation/
│ │ ├── screens/
│ │ │ └── eta_home_screen.dart
│ │ └── providers/
│ │ ├── eta_provider.dart
│ │ └── ws_provider.dart
│ │
│ └── recycling_guide/ # Guía offline (Persona D)
│ ├── domain/
│ ├── data/
│ └── presentation/
│ ├── screens/
│ │ ├── recycling_guide_screen.dart
│ │ └── category_detail_screen.dart
│ └── providers/
│ └── recycling_provider.dart
└── main.dart
```
---
## 6. Configurar `main.dart`
```dart
// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'core/theme/app_theme.dart';
import 'features/eta/presentation/screens/eta_home_screen.dart';
import 'features/recycling_guide/presentation/screens/recycling_guide_screen.dart';
void main() {
runApp(const ProviderScope(child: MyApp()));
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
title: 'Basura App',
theme: AppTheme.lightTheme, // ← Tema de Persona D
routerConfig: _router,
debugShowCheckedModeBanner: false,
);
}
}
// Router (Persona C configura las rutas finales)
final _router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (_, __) => const ETAHomeScreen(), // Persona C
),
GoRoute(
path: '/guia',
builder: (_, __) => const RecyclingGuideScreen(), // Persona D
),
],
);
```
---
## 7. Actualizar la URL del backend
**Importante:** Por defecto, el backend corre en `localhost:8000`, pero:
- **Emulador Android:** usa `10.0.2.2:8000`
- **Emulador iOS:** usa `localhost:8000`
- **Dispositivo físico:** usa la IP de tu PC en la red local (ej: `192.168.1.10:8000`)
### Crear un provider de configuración
```dart
// lib/core/config/api_config.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dio/dio.dart';
// Cambia esto según tu entorno
const _baseUrl = 'http://10.0.2.2:8000'; // Android emulator
// const _baseUrl = 'http://localhost:8000'; // iOS simulator
// const _baseUrl = 'http://192.168.1.10:8000'; // Dispositivo físico
final apiBaseUrlProvider = Provider<String>((ref) => _baseUrl);
final dioProvider = Provider<Dio>((ref) {
return Dio(BaseOptions(
baseUrl: ref.watch(apiBaseUrlProvider),
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 10),
));
});
```
Persona C importa este provider en sus datasources.
---
## 8. Correr la app
### En un emulador
```bash
# Listar dispositivos disponibles
fvm flutter devices
# Correr en el primer dispositivo
fvm flutter run
# O seleccionar uno específico
fvm flutter run -d <device-id>
```
### En dispositivo físico
1. Habilita **USB Debugging** en el teléfono
2. Conecta el cable
3. `fvm flutter run`
---
## 9. Verificar que funciona
### Módulo D (offline — funciona sin backend)
1. Navega a `/guia` (botón en home o URL directa)
2. Deberías ver 4 categorías: Orgánicos, Reciclables, Sanitarios, Especiales
3. Prueba el buscador: escribe "pila" → debe mostrar "Especiales"
4. Toca una categoría → pantalla de detalle con items ✓ y ✗
### Módulo C (requiere backend corriendo)
1. **Arranca el backend primero:**
```bash
cd basura_backend
uvicorn app.main:app --reload
```
2. **Arranca el simulador:**
```bash
curl -X POST http://localhost:8000/admin/route/RUTA-01/start
```
3. En Flutter, deberías ver:
- Ventana horaria: "7:20 pm 7:35 pm"
- Mensaje: "El camión está en camino..."
- Badge de estado: "EN_RUTA"
4. Cada ~10 segundos, el ETA se actualiza automáticamente vía WebSocket
---
## 10. Hot Reload y Hot Restart
### Hot Reload (r)
Actualiza cambios en la UI sin perder el estado.
```bash
# En la terminal donde corre flutter run, presiona:
r
```
### Hot Restart (R)
Reinicia la app desde cero.
```bash
R
```
---
## 11. Debugging
### Ver logs en consola
```bash
fvm flutter run --verbose
```
### Flutter DevTools
```bash
fvm flutter pub global activate devtools
fvm flutter pub global run devtools
```
Abre el link que aparece en el navegador.
---
## 12. Build para producción
### Android APK
```bash
fvm flutter build apk --release
```
El APK estará en: `build/app/outputs/flutter-apk/app-release.apk`
### iOS (requiere macOS + Xcode)
```bash
fvm flutter build ios --release
```
---
## 13. Troubleshooting
### "Waiting for another flutter command to release the startup lock"
```bash
# Borrar el lock file
rm ~/.dart_tool/dart_tool.lock
```
### "Gradle build failed"
Java incorrecto o problema de versión.
**Solución:**
```bash
# Verificar Java 17
java -version
# Limpiar build
cd android
./gradlew clean
cd ..
fvm flutter clean
fvm flutter pub get
```
### "Could not connect to WebSocket"
Backend no está corriendo o URL incorrecta.
**Solución:**
```bash
# Verifica que el backend esté up
curl http://localhost:8000/health
# Ajusta la URL en api_config.dart según tu entorno
```
### "asset not found: assets/recycling_guide.json"
El asset no está declarado en `pubspec.yaml`.
**Solución:**
```yaml
flutter:
assets:
- assets/recycling_guide.json
```
Luego:
```bash
fvm flutter clean
fvm flutter pub get
```
### "Unsupported class file major version 65"
Estás usando Java 21 en lugar de Java 17.
**Solución:**
```bash
# Cambiar a Java 17 (ver setup/00-requisitos.md)
sudo update-alternatives --config java
```
---
## 14. Integración final con Persona A
Cuando Persona A tenga el módulo de Auth listo:
1. **Agregar el JWT token al Dio:**
```dart
final dioProvider = Provider<Dio>((ref) {
final dio = Dio(BaseOptions(baseUrl: _baseUrl));
// Interceptor para agregar token
dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
final token = ref.read(authTokenProvider);
if (token != null) {
options.headers['Authorization'] = 'Bearer $token';
}
handler.next(options);
},
));
return dio;
});
```
2. **Proteger las rutas:**
```dart
GoRoute(
path: '/',
redirect: (context, state) {
final isLoggedIn = ref.read(authProvider).isAuthenticated;
return isLoggedIn ? null : '/login';
},
builder: (_, __) => const ETAHomeScreen(),
),
```
---
## Siguiente paso
- [Variables de entorno](03-env.md) — configurar `.env` para dev/prod
- [Contratos de API](../api/01-endpoints.md) — consumir endpoints REST
- [WebSocket protocol](../api/02-websocket.md) — conectar en tiempo real