2026-05-16 16:43:26 +02:00
# SOP — BitePlan (Flutter → Android APK)
2026-03-24 23:11:46 +01:00
## Scope
2026-05-16 16:43:26 +02:00
App mobile Android con tre funzionalità:
2026-03-24 23:11:46 +01:00
1. **Meal Planner ** — pianificazione settimanale (7 giorni × 3 pasti: colazione, pranzo, cena). Ogni pasto contiene una lista di voci testuali liberamente modificabili.
2026-05-16 16:43:26 +02:00
2. **Conversione crudo/cotto ** — calcolo del peso cotto a partire dal peso crudo (e viceversa) tramite coefficienti di resa definiti in un asset JSON interno.
2026-03-24 23:11:46 +01:00
3. **Lista della spesa ** — checklist con aggiunta, spunta e rimozione elementi.
---
## Stack tecnologico
| Livello | Scelta |
|---|---|
2026-05-16 16:43:26 +02:00
| Framework | Flutter (Dart) |
| Stato | Provider o Riverpod |
| Persistenza | shared_preferences |
| UI | Material 3, mobile-first |
| Build APK | Docker (container build) |
| Sviluppo | Docker + noVNC (container dev con GUI) |
2026-03-24 23:11:46 +01:00
---
## Struttura progetto
```
2026-05-16 16:43:26 +02:00
lib/
├── main.dart
├── app.dart # MaterialApp, routing, BottomNavigationBar
2026-03-24 23:11:46 +01:00
├── pages/
2026-05-16 16:43:26 +02:00
│ ├── meal_planner_page.dart
│ ├── converter_page.dart
│ └── shopping_list_page.dart
├── widgets/
│ ├── meal_card.dart # Accordion giorno
│ └── checkbox_item.dart
├── models/
│ ├── meal_plan.dart
│ └── shopping_item.dart
├── services/
│ ├── storage_service.dart # Wrapper shared_preferences
│ └── conversion_service.dart # rawToCooked / cookedToRaw
└── data/
└── conversions.json # Asset: 50+ alimenti × metodi cottura
docker/
├── dev/
│ ├── Dockerfile # Flutter SDK + Android SDK + Xvfb + noVNC
│ └── docker-compose.yml # Porta 6080 → noVNC, volume su ./
├── build/
│ ├── Dockerfile # Flutter SDK + Android SDK headless
│ └── build.sh # flutter build apk --release + firma
pubspec.yaml
2026-03-24 23:11:46 +01:00
```
---
2026-05-16 16:43:26 +02:00
## Container dev (noVNC)
Il container di sviluppo espone un desktop virtuale via browser, utile per eseguire l'emulatore Android o Android Studio senza installare nulla sull'host.
**Stack interno ** : Ubuntu + Flutter SDK + Android SDK + Xvfb + noVNC + websockify.
2026-03-24 23:11:46 +01:00
``` bash
2026-05-16 16:43:26 +02:00
# Avvio
cd docker/dev
docker compose up
# Accesso GUI
# → http://localhost:6080 (noVNC, nessuna password)
2026-03-24 23:11:46 +01:00
```
2026-05-16 16:43:26 +02:00
Il volume mappa `./` (root del progetto) in `/workspace` nel container, quindi le modifiche ai file sono bidirezionali in tempo reale.
**Workflow nel container ** :
1. Aprire un terminale nel desktop noVNC
2. `cd /workspace && flutter pub get`
3. Avviare l'emulatore o collegare device via ADB
4. `flutter run`
2026-03-24 23:11:46 +01:00
---
2026-05-16 16:43:26 +02:00
## Container build (APK)
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
Build headless riproducibile, senza GUI.
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
``` bash
# Debug
bash docker/build/build.sh
# Release firmato
bash docker/build/build.sh --release
2026-03-24 23:11:46 +01:00
```
2026-05-16 16:43:26 +02:00
Pipeline interna:
1. `flutter pub get`
2. `flutter build apk --release` → `build/app/outputs/flutter-apk/app-release.apk`
3. Firma con `apksigner` (keystore montato come volume o secret)
4. `zipalign`
L'APK firmato viene copiato in `dist/` nella root del progetto.
2026-03-24 23:11:46 +01:00
---
2026-05-16 16:43:26 +02:00
## Modello dati
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
**Meal Plan **
``` dart
class MealPlan {
final Map < String , DayPlan > days ; // 'lunedi' … 'domenica'
}
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
class DayPlan {
final List < String > colazione ;
final List < String > pranzo ;
final List < String > cena ;
2026-03-24 23:11:46 +01:00
}
```
2026-05-16 16:43:26 +02:00
**Shopping Item **
``` dart
class ShoppingItem {
final String id ;
final String name ;
bool checked ;
}
2026-03-24 23:11:46 +01:00
```
---
2026-05-16 16:43:26 +02:00
## Conversione crudo/cotto
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
**Asset ** — `lib/data/conversions.json`
``` json
{
"pollo" : { "forno" : { "yield" : 0.75 } , "padella" : { "yield" : 0.70 } } ,
"riso basmati" : { "bollitura" : { "yield" : 3.00 } }
}
2026-03-24 23:11:46 +01:00
```
2026-05-16 16:43:26 +02:00
`yield = peso_cotto / peso_crudo`
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
**Service ** — `lib/services/conversion_service.dart`
``` dart
double rawToCooked ( double raw , double yield ) = > raw * yield ;
double cookedToRaw ( double cooked , double yield ) = > cooked / yield ;
```
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
UX: ricerca testuale → selezione alimento + metodo → input grammi → risultato in tempo reale.
2026-03-24 23:11:46 +01:00
---
2026-05-16 16:43:26 +02:00
## Persistenza
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
`StorageService` wrappa `shared_preferences` :
- Meal plan serializzato come JSON stringa sotto chiave `meals`
- Lista spesa serializzata come JSON stringa sotto chiave `shopping_list`
2026-03-24 23:11:46 +01:00
---
2026-05-16 16:43:26 +02:00
## UI
2026-03-24 23:11:46 +01:00
2026-05-16 16:43:26 +02:00
- `BottomNavigationBar` con 3 tab: Pasti | Converti | Spesa
- Material 3, `ColorScheme.fromSeed(seedColor: Color(0xFF2d6a4f))`
- Target touch minimo 48× 48 dp (Material baseline)
- Orientamento bloccato in portrait (`SystemChrome.setPreferredOrientations` )
2026-03-24 23:11:46 +01:00
---
## Fasi future
- Modifica coefficienti di conversione in-app
- Calcolo kcal
2026-05-16 16:43:26 +02:00
- Condivisione piano via QR code
2026-03-24 23:11:46 +01:00
- Sync cloud