Richiamo all’architettura dell’app Flutter
Obiettivo
Strutturare correttamente il progetto per facilitare manutenzione, riuso, testing e scalabilità.
Perché serve un’architettura?
- Codice più leggibile
- Facile separare UI e logica
- Collaborazione tra team
- Scalabilità e test automatizzati
Struttura a cartelle base
| Cartella | Contenuto |
|---|---|
lib/screens/ | Le schermate (UI) |
lib/models/ | Strutture dati (classi, DTO, entity) |
lib/services/ | Chiamate API, DB, Firebase |
lib/widgets/ | Componenti riutilizzabili |
lib/providers/ | Logica di stato se si usa Provider |
In alternativa:
lib/feature/nome/per struttura modulare (feature-based)
Separation of concerns
Ogni parte del codice ha un singolo scopo:
| Livello | Responsabilità |
|---|---|
| UI (screens) | Visualizza e riceve input |
| Logic | Gestisce eventi, stato, validazione |
| Data layer | Interroga API, DB, storage |
Clean Architecture base
| Strato | Descrizione |
|---|---|
| Presentation | UI + ViewModel |
| Domain | Entità, UseCases, logica pura |
| Data | Repository, sorgente dati (API, DB, Firebase) |
Vantaggi:
- Codice testabile
- Decoupling
- Manutenibilità nel lungo periodo
Esempio pratica: app Note
| Cartella | Cosa contiene |
|---|---|
screens/note_list.dart | UI della lista note |
models/note.dart | Modello Note |
services/note_api.dart | Funzione per caricare/salvare |
providers/note_provider.dart | Logica con ChangeNotifier |
Consigli
Usa extension o helper per pulire la UI
- Extension: aggiungi funzionalità a tipi esistenti per scrivere codice più leggibile.
extension Spacing on num {
SizedBox get h => SizedBox(height: toDouble());
SizedBox get w => SizedBox(width: toDouble());
}
Column(
children: [
Text('Titolo'),
16.h, // invece di SizedBox(height: 16)
Text('Descrizione'),
],
)- Helper: funzioni riutilizzabili per widget personalizzati.
//File: nome_helper.dart
Widget customButton(String label, VoidCallback onPressed) {
return ElevatedButton(
onPressed: onPressed,
child: Text(label.toUpperCase()),
);
}
//File:screen
customButton("Login", () => print("Premuto"));Non mischiare setState, Provider, Bloc, ecc.
Usare troppi approcci per la gestione dello stato nello stesso progetto crea confusione, rende il debug più difficile e complica l’onboarding di altri sviluppatori.
Scegli un paradigma coerente per tutto il progetto, ed eventualmente usa setState solo per widget locali molto semplici.
- Nomina coerente (es.
NoteService,NoteScreen,NoteModel) - Ogni file dovrebbe contenere una sola responsabilità