Files
biteplan/CLAUDE.md
T

94 lines
6.3 KiB
Markdown
Raw Normal View History

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
BitePlan è un'app Android per meal planning, conversione crudo/cotto e lista della spesa, scritta in Flutter. Tutta la UI e la UX sono in **italiano**. Il rilascio avviene come APK Android buildato via Docker.
## Ruolo atteso
Quando viene richiesta una modifica o una nuova funzionalità, segui questo flusso:
1. **Analizza prima di implementare**: valuta se la richiesta ha senso — fattibilità, complessità reale, impatto su architettura/UX/persistenza. Se esiste un modo migliore di implementarla, proponilo; se la richiesta è mal posta o ha un costo sproporzionato al beneficio, dillo esplicitamente con l'alternativa. Motiva i giudizi con riferimenti concreti al codice.
2. **Implementa da esperto**: se la richiesta va bene (o dopo l'eventuale discussione), implementala seguendo le convenzioni del progetto (feature-first, Provider, tutto in italiano).
3. **Suggerisci i test, non scriverli**: al termine indica quali test coprirebbero la modifica (casi e file dove andrebbero), ma implementali solo se richiesto esplicitamente.
## Comandi
```bash
# Sviluppo (host, no Docker)
emulator -avd biteplan & # AVD dedicato, Pixel 6 / Android 14 (google_apis x86_64)
flutter run # hot reload con "r", hot restart con "R"
# Analisi statica
flutter analyze # lint: package:flutter_lints/flutter.yaml
# Test rapidi (host)
flutter test # tutta la suite
flutter test test/features/meal_planner/qr_test.dart # singolo file
# Test riproducibili (Docker, immagine biteplan-build — usati come riferimento)
docker run --rm -v "$(pwd):/workspace" -w /workspace biteplan-build \
bash -c "flutter pub get --enforce-lockfile && flutter test"
# Integration test (richiede emulatore avviato)
flutter test integration_test/app_test.dart
# Build APK (headless, via Docker)
bash docker/build.sh # debug → dist/biteplan-debug.apk
export BITEPLAN_KEYSTORE_PASS=password # richiesto solo per --release
bash docker/build.sh --release # release → dist/biteplan-release.apk
```
## Sviluppo locale
- **Flutter host**: installato in `~/flutter` (tag `3.41.9`, stessa versione pinnata nel Dockerfile), nel `PATH` via `~/.bashrc`. Docker non si usa più per il dev; resta per test di riferimento e build APK (isolamento, `--enforce-lockfile`, firma release).
- **Emulatore**: Android SDK in `/home/davide/android-sdk`; accelerazione hardware via `/dev/kvm` (WSL2 con KVM abilitato).
- **AVD `biteplan`**: dedicato a questo progetto (Pixel 6, Android 14, google_apis/x86_64). Non riutilizzare AVD di altri progetti (es. `palladium_wallet`).
- **Permessi**: le build Docker girano come root nel volume montato — se `.dart_tool/` o `build/` risultano di proprietà root, serve `sudo chown -R davide:davide .dart_tool build` prima di usare flutter da host.
## Build e firma
- **Flutter pinnato**: versione `3.41.9` in `docker/Dockerfile` (`ARG FLUTTER_VERSION`); aggiornare anche `.flutter-version` e l'install host
- **Dipendenze**: `pubspec.lock` committato; la build usa `--enforce-lockfile` e fallisce se il lock non è allineato
- **Keystore**: `docker/biteplan.jks` è in `.gitignore`; da ottenere dall'autore o generare con `keytool`. Password via `BITEPLAN_KEYSTORE_PASS` (mai hardcoded); alias: `biteplan`
- **applicationId**: `com.davide.biteplan` — non cambiare, garantisce aggiornamento diretto dalla v1.2.1
- **versionCode**: letto da `pubspec.yaml` (`version: X.Y.Z+BUILD`); BUILD deve essere sempre crescente
## Architettura
Feature-first sotto `lib/features/` (meal_planner, converter, shopping_list, guide), ognuna con `models/`, `providers/`, `presentation/`. Codice trasversale in `lib/shared/` e `lib/core/`.
```
lib/
├── app.dart # MaterialApp + NavigationBar; in initState chiama UpdateService.checkUpdate() e mostra showUpdateDialog()
├── core/constants/app_constants.dart # kDayIds, kMealSlots, kStorageKey*, kAppVersion
├── shared/
│ ├── services/storage_service.dart # wrapper SharedPreferences (load/save)
│ ├── services/update_service.dart # checkUpdate() → GitHub API; isNewer(), parseTagName() (@visibleForTesting)
│ ├── services/url_launcher_service.dart # MethodChannel 'com.davide.biteplan/launcher' → Intent.ACTION_VIEW
│ └── widgets/update_dialog.dart # showUpdateDialog() — "Più tardi" / "Scarica"
├── features/
│ ├── meal_planner/ # MealPlan/DayPlan, qr_codec.dart (buildQrPayload, parseMealPlanFromQr), pagine planner + scan QR
│ ├── converter/ # ConversionEntry con rawToCooked/cookedToRaw, dati da assets/data/conversions.json
│ ├── shopping_list/ # ShoppingItem con quantity per aggregazione duplicati
│ └── guide/ # guide_page (3 tab) + info_bottom_sheet
└── assets/data/conversions.json # 50+ alimenti × metodi cottura
```
**State management**: Provider (`ChangeNotifier`), uno per feature, registrati in `main.dart` con `MultiProvider`.
**Persistenza**: `shared_preferences`, chiavi `meals` e `shopping_list` (JSON serializzato).
**Conversione**: logica in `ConversionEntry``rawToCooked = raw * yieldFactor`, `cookedToRaw = cooked / yieldFactor`.
**UI**: Material 3, seed color `Color(0xFF2d6a4f)`, tutto in italiano.
**QR**: payload JSON `{ "v": 1, "meals": { ... } }`, limite 2953 byte (capacità QR con error correction L).
**Update checker**: `UpdateService.checkUpdate()` usa `dart:io` `HttpClient` (nessun package aggiuntivo); disabilitato su web (`kIsWeb`). Il `MethodChannel` `com.davide.biteplan/launcher` è implementato in `android/app/src/main/kotlin/com/davide/biteplan/MainActivity.kt`.
## Testing
130 test unit + widget in `test/` (rispecchia la struttura di `lib/`), più un integration test in `integration_test/app_test.dart`. I test unit/widget non richiedono device.
- Storage isolato con `SharedPreferences.setMockInitialValues({})`
- Widget test: estensione `pumpApp` in `test/helpers/pump_app.dart`
- MethodChannel nei test: `TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler` — vedi `test/shared/widgets/update_dialog_test.dart`