Lezione 15: Progetto — Sviluppo e completamento
Obiettivi
Section titled “Obiettivi”- Implementare tutte le funzionalità del progetto
- Aggiungere persistenza dei dati
- Testare e correggere bug
- Rifinire l’interfaccia utente
1. Roadmap dello sviluppo
Section titled “1. Roadmap dello sviluppo”Segui questa roadmap per completare il progetto:
LEZIONE 15a (6 ore): Core funzionalità └── Implementa le funzionalità principali (CRUD) └── Aggiungi persistenza JSON └── Testa che tutto funzioni
LEZIONE 15b (6 ore): UI e rifiniture └── Migliora l'aspetto (tema, colori, layout) └── Aggiungi SnackBar per feedback └── Gestisci casi limite (input vuoto, errori) └── Test finale su tutte le funzionalità2. Pattern CRUD: le 4 operazioni fondamentali
Section titled “2. Pattern CRUD: le 4 operazioni fondamentali”Quasi tutti i progetti hanno bisogno di operazioni CRUD (Create, Read, Update, Delete):
# CREATE — Aggiungeredef aggiungi(e): if input.value: dati.append({ "id": len(dati) + 1, "testo": input.value, "creato": str(datetime.date.today()), }) salva_dati(dati) ricostruisci_ui() mostra_snack("✅ Elemento aggiunto") input.value = "" page.update()
# READ — Leggere (già fatto: carica_dati all'avvio)dati = carica_dati()
# UPDATE — Modificaredef modifica(idx): nuovo_valore = input_modifica.value if nuovo_valore: dati[idx]["testo"] = nuovo_valore salva_dati(dati) ricostruisci_ui() mostra_snack("✏️ Modificato!")
# DELETE — Eliminaredef elimina(idx): dati.pop(idx) salva_dati(dati) ricostruisci_ui() mostra_snack("🗑️ Eliminato!")3. Pattern: Aggiornare la UI dopo modifiche
Section titled “3. Pattern: Aggiornare la UI dopo modifiche”La funzione ricostruisci_ui() è fondamentale:
def ricostruisci_ui(): """Ricostruisce tutta la UI partendo dai dati correnti.""" contenuto.controls.clear()
if not dati: contenuto.controls.append( ft.Text("Nessun elemento. Aggiungine uno!", color="grey", italic=True) ) page.update() return
for i, elemento in enumerate(dati): card = ft.Container( content=ft.Row([ ft.Text(elemento["testo"], expand=True), ft.IconButton(ft.icons.EDIT, on_click=lambda e, idx=i: modifica(idx)), ft.IconButton(ft.icons.DELETE, on_click=lambda e, idx=i: elimina(idx)), ]), padding=15, bgcolor="white", border_radius=10, margin=5, ) contenuto.controls.append(card)
page.update()4. Helper: funzione SnackBar
Section titled “4. Helper: funzione SnackBar”Aggiungi questa utility al tuo progetto:
def mostra_snack(page, messaggio, colore="green"): """Mostra una notifica temporanea.""" page.snack_bar = ft.SnackBar( content=ft.Text(messaggio), open=True, bgcolor=colore, duration=3000, ) page.update()Esempio d’uso:
mostra_snack(page, "✅ Salvato!", "green")mostra_snack(page, "❌ Errore!", "red")mostra_snack(page, "⚠️ Campo vuoto!", "orange")5. Pattern: Conferma prima di eliminare
Section titled “5. Pattern: Conferma prima di eliminare”def chiedi_elimina(idx): def conferma(e): dialog.open = False dati.pop(idx) salva_dati(dati) ricostruisci_ui() mostra_snack(page, "🗑️ Eliminato!") page.update()
dialog = ft.AlertDialog( title=ft.Text("Conferma"), content=ft.Text("Sei sicuro di voler eliminare questo elemento?"), actions=[ ft.TextButton("Annulla", on_click=lambda e: chiudi(dialog)), ft.ElevatedButton("Elimina", on_click=conferma, color="red"), ], actions_alignment=ft.MainAxisAlignment.END, ) page.dialog = dialog dialog.open = True page.update()
def chiudi(dialog): dialog.open = False page.update()6. Gestione errori: try/except
Section titled “6. Gestione errori: try/except”Proteggi le operazioni critiche:
def carica_dati_sicura(): try: if os.path.exists(FILE_DATI): with open(FILE_DATI, "r", encoding="utf-8") as f: return json.load(f) except json.JSONDecodeError: mostra_snack(page, "⚠️ File dati danneggiato. Ricreo...", "orange") except Exception as ex: mostra_snack(page, f"❌ Errore: {ex}", "red") return []def salva_dati_sicura(dati): try: # Prima salva in un file temporaneo temp_file = FILE_DATI + ".tmp" with open(temp_file, "w", encoding="utf-8") as f: json.dump(dati, f, indent=2, ensure_ascii=False) # Poi rinomina (operazione atomica) os.replace(temp_file, FILE_DATI) except Exception as ex: mostra_snack(page, f"❌ Errore salvataggio: {ex}", "red")7. Checklist qualità
Section titled “7. Checklist qualità”Usa questa checklist per assicurarti che il progetto sia completo:
✅ Funzionalità
Section titled “✅ Funzionalità”- L’app fa ciò che ho descritto nel wireframe
- Tutti i bottoni funzionano
- La navigazione tra schermate funziona
- I dati vengono salvati su file JSON
- Alla riapertura, i dati sono ancora lì
✅ UI/UX
Section titled “✅ UI/UX”- Layout pulito (padding, spacing adeguati)
- Card con ombre e angoli arrotondati
- Messaggi di feedback (SnackBar) per azioni importanti
- Messaggio quando la lista è vuota
- Colori coerenti in tutta l’app
✅ Robustezza
Section titled “✅ Robustezza”- Se l’input è vuoto, mostra un messaggio (non si blocca)
- Se il file JSON è danneggiato, l’app non si blocca
- Conferma prima di eliminare elementi
- Gestione errori per API (se presenti)
✅ Extra (opzionali)
Section titled “✅ Extra (opzionali)”- Tema chiaro/scuro
- Filtri o ricerca
- Ordinamento elementi
- Animazioni
- Icone appropriate
8. Esempio: template progetto completo
Section titled “8. Esempio: template progetto completo”import flet as ftimport jsonimport osimport datetime
FILE_DATI = "progetto_dati.json"
# --- FUNZIONI DI UTILITY ---
def carica_dati(): try: if os.path.exists(FILE_DATI): with open(FILE_DATI, "r", encoding="utf-8") as f: return json.load(f) except json.JSONDecodeError: pass return []
def salva_dati(dati): with open(FILE_DATI, "w", encoding="utf-8") as f: json.dump(dati, f, indent=2, ensure_ascii=False)
def mostra_snack(page, msg, colore="green"): page.snack_bar = ft.SnackBar(ft.Text(msg), open=True, bgcolor=colore, duration=3000) page.update()
# --- MAIN APP ---
def main(page: ft.Page): page.title = "Il Mio Progetto" page.padding = 20 page.theme_mode = ft.ThemeMode.LIGHT page.scroll = ft.ScrollMode.AUTO
dati = carica_dati() contenuto = ft.Column(spacing=10, expand=True)
# --- FUNZIONI DELL'APP ---
def ricostruisci_ui(): """Ricostruisce la UI dai dati.""" contenuto.controls.clear() if not dati: contenuto.controls.append( ft.Text("Nessun elemento presente", italic=True, color="grey") ) page.update() return
for i, elem in enumerate(dati): # Personalizza in base al tuo progetto card = ft.Container( content=ft.Text(f"{i+1}. {elem.get('testo', '')}"), padding=15, bgcolor="white", border_radius=10, shadow=ft.BoxShadow(blur_radius=5, color=ft.colors.GREY_300), ) contenuto.controls.append(card) page.update()
# --- INTERFACCIA ---
page.add( ft.Text("📱 Il Mio Progetto", size=28, weight="bold"), ft.Divider(), contenuto, )
ricostruisci_ui()
ft.app(target=main)9. Esercizio
Section titled “9. Esercizio”🎯 Compito: Completare il progetto
Section titled “🎯 Compito: Completare il progetto”Nelle prossime 2 settimane (12 ore totali):
Settimana 7 (6 ore) — Funzionalità core:
- Implementa le 3-5 funzionalità principali della tua app
- Aggiungi persistenza JSON
- Testa che ogni funzionalità funzioni correttamente
Settimana 8 (6 ore) — Rifiniture:
- Migliora UI (colori, layout, ombre)
- Aggiungi SnackBar per feedback
- Gestisci errori e casi limite
- Test finale completo
Ogni mezz’ora fai un test veloce: avvia l’app e verifica che non si blocchi.