Files
biteplan/CLAUDE.md
T
davide cd82edd8d3 chore: bump versione a 2.1.0 e aggiorna CLAUDE.md
versionCode 10300 → 10400. CLAUDE.md aggiornato con i nuovi servizi
shared (UpdateService, UrlLauncherService, update_dialog), il
MethodChannel in MainActivity.kt e il pattern di mock per widget test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 08:55:23 +02:00

117 lines
6.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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**. L'obiettivo finale è un APK Android buildabile via Docker.
## Comandi
```bash
# Sviluppo (web server con hot reload)
cd docker/dev && docker compose up
# → http://localhost:5173
# Con il container attivo: docker compose attach dev → r = hot reload
# Test (dalla root del progetto — richiede immagine biteplan-build)
docker run --rm -v "$(pwd):/workspace" -w /workspace biteplan-build \
bash -c "flutter pub get --enforce-lockfile && flutter test"
# Test singolo file
docker run --rm -v "$(pwd):/workspace" -w /workspace biteplan-build \
bash -c "flutter test test/features/meal_planner/qr_test.dart"
# Build APK (headless, da host)
bash docker/build/build.sh # debug → dist/biteplan-debug.apk
export BITEPLAN_KEYSTORE_PASS=password # richiesto solo per --release
bash docker/build/build.sh --release # release → dist/biteplan-release.apk
```
## Build e firma
- **Flutter pinnato**: versione `3.41.9` nei Dockerfile (`ARG FLUTTER_VERSION`); aggiornare anche `.flutter-version`
- **Gradle cache**: named volume Docker `gradle-cache`, isolata dall'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`
- **Firma release**: password passata via `BITEPLAN_KEYSTORE_PASS` (mai hardcoded); alias keystore: `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
```
lib/
├── main.dart
├── app.dart # MaterialApp, NavigationBar; initState chiama UpdateService.checkUpdate() e mostra showUpdateDialog() se c'è aggiornamento
├── core/
│ ├── constants/app_constants.dart # kDayIds, kMealSlots, kStorageKey*, kAppVersion
│ └── theme/app_theme.dart
├── shared/
│ ├── services/
│ │ ├── storage_service.dart # wrapper SharedPreferences (load/save)
│ │ ├── update_service.dart # checkUpdate() → GitHub API, isNewer(), parseTagName() (@visibleForTesting)
│ │ └── url_launcher_service.dart # MethodChannel 'com.davide.biteplan/launcher' → Intent.ACTION_VIEW
│ └── widgets/
│ └── update_dialog.dart # showUpdateDialog() — AlertDialog con "Più tardi" / "Scarica"
├── features/
│ ├── meal_planner/
│ │ ├── models/meal_plan.dart # MealPlan, DayPlan
│ │ ├── providers/meal_planner_provider.dart
│ │ ├── qr_codec.dart # buildQrPayload, parseMealPlanFromQr
│ │ └── presentation/
│ │ ├── pages/meal_planner_page.dart
│ │ ├── pages/qr_scan_page.dart
│ │ └── widgets/meal_card.dart, qr_share_sheet.dart
│ ├── converter/
│ │ ├── models/conversion_entry.dart # rawToCooked, cookedToRaw (metodi sul model)
│ │ ├── providers/converter_provider.dart
│ │ └── presentation/pages/converter_page.dart
│ ├── shopping_list/
│ │ ├── models/shopping_item.dart # quantity per aggregazione duplicati
│ │ ├── providers/shopping_list_provider.dart
│ │ └── presentation/
│ │ ├── pages/shopping_list_page.dart
│ │ └── widgets/shopping_item_tile.dart
│ └── guide/
│ └── presentation/
│ ├── pages/guide_page.dart # 3 tab: Pasti, Converti, Spesa
│ └── widgets/info_bottom_sheet.dart # aperto dal bottone info in AppBar
└── assets/data/conversions.json # 50+ alimenti × metodi cottura
```
**State management**: Provider (`ChangeNotifier`).
**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`.
**Widget test con MethodChannel**: usare `TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler` per mockare il canale nei test — vedi `test/shared/widgets/update_dialog_test.dart`.
## Testing
130 test (unit + widget). Non richiedono device fisico.
```
test/
├── helpers/pump_app.dart # estensione pumpApp per widget test
├── features/
│ ├── converter/
│ │ ├── models/conversion_entry_test.dart
│ │ └── providers/converter_provider_test.dart
│ ├── meal_planner/
│ │ ├── models/meal_plan_test.dart
│ │ ├── providers/meal_planner_provider_test.dart
│ │ ├── widgets/meal_card_test.dart
│ │ └── qr_test.dart
│ └── shopping_list/
│ ├── models/shopping_item_test.dart
│ ├── providers/shopping_list_provider_test.dart
│ └── widgets/shopping_item_tile_test.dart
└── shared/
├── services/update_service_test.dart
└── widgets/update_dialog_test.dart
```
I test usano `SharedPreferences.setMockInitialValues({})` per isolare lo storage.