Añadir 02-frontend.md
442
02-frontend.md.-.md
Normal file
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
|
||||
Reference in New Issue
Block a user