Skip to content

Lezione 4: Eventi e stato — Rendere l'app interattiva

  • Capire cosa sono gli eventi in Flet
  • Gestire click, cambiamenti di testo e altri eventi
  • Aggiornare la UI dopo un evento con page.update()
  • Usare page.controls per gestire dinamicamente i componenti

In un’applicazione desktop/mobile, il programma reagisce a ciò che fa l’utente:

  • Clicca un bottone → esegui una funzione
  • Scrive in un campo → aggiorna un’altra parte della UI
  • Seleziona un’opzione → mostra/nasconde elementi

Questo si chiama programmazione a eventi (event-driven).

def nome_funzione(e):
# 'e' contiene informazioni sull'evento
# fai qualcosa...
page.update() # <-- Fondamentale!

Il parametro e (evento) contiene informazioni utili come:

  • e.control — il componente che ha generato l’evento
  • e.control.value — il valore corrente del componente
  • e.data — dati aggiuntivi dell’evento

import flet as ft
def main(page: ft.Page):
def bottone_cliccato(e):
print("Hai cliccato il bottone!")
page.add(ft.Text("Bottone cliccato!", color="green"))
page.add(
ft.ElevatedButton("Cliccami", on_click=bottone_cliccato)
)
ft.app(target=main)
ft.ElevatedButton(
"Cliccami",
on_click=lambda e: page.add(ft.Text("Click!"))
)

Nota: lambda è utile per operazioni semplici. Per logiche più complesse, usa una funzione separata.


3. Stato locale: ricordare i valori tra gli eventi

Section titled “3. Stato locale: ricordare i valori tra gli eventi”

Lo stato è l’insieme dei dati che la nostra app ricorda tra un evento e l’altro.

import flet as ft
def main(page: ft.Page):
contatore = 0 # <-- Stato (variabile nella funzione)
def incrementa(e):
nonlocal contatore # Serve per modificare la variabile
contatore += 1
testo_contatore.value = f"Hai cliccato {contatore} volte"
page.update()
testo_contatore = ft.Text("Hai cliccato 0 volte", size=20)
bottone = ft.ElevatedButton("+1", on_click=incrementa)
page.add(testo_contatore, bottone)
ft.app(target=main)

Importante: nonlocal serve perché contatore è definita nella funzione main e la modifichiamo dentro incrementa. Senza nonlocal, Python creerebbe una nuova variabile locale.

Usare attributi di oggetti (alternativa più pulita)

Section titled “Usare attributi di oggetti (alternativa più pulita)”
import flet as ft
class ContatoreApp:
def __init__(self, page: ft.Page):
self.page = page
self.contatore = 0
self.page.title = "Contatore"
self.page.padding = 30
self.testo = ft.Text("0", size=50, weight="bold")
self.page.add(
self.testo,
ft.Row([
ft.IconButton(ft.icons.REMOVE, on_click=self.decrementa),
ft.IconButton(ft.icons.ADD, on_click=self.incrementa),
], alignment=ft.MainAxisAlignment.CENTER),
)
def incrementa(self, e):
self.contatore += 1
self.testo.value = str(self.contatore)
self.page.update()
def decrementa(self, e):
self.contatore -= 1
self.testo.value = str(self.contatore)
self.page.update()
ft.app(target=ContatoreApp)

Per il corso useremo principalmente il primo approccio (variabili + nonlocal), che è più semplice e non richiede classi.


4. page.update() — La chiamata fondamentale

Section titled “4. page.update() — La chiamata fondamentale”

page.update() dice a Flet di ridisegnare la pagina con i nuovi valori. Senza questa chiamata, le modifiche ai componenti non si vedono!

# ERRORE: il testo non viene aggiornato!
def incrementa(e):
contatore += 1
testo.value = str(contatore) # Modifica, ma nessun aggiornamento visivo
# CORRETTO
def incrementa(e):
contatore += 1
testo.value = str(contatore)
page.update() # Ora la UI si aggiorna!

Regola d’oro: Dopo aver modificato qualsiasi proprietà di un componente (value, color, visible, ecc.), chiama sempre page.update().


5. page.controls: gestire la lista dei componenti

Section titled “5. page.controls: gestire la lista dei componenti”

page.controls è una lista di tutti i componenti nella pagina. Possiamo modificarla dinamicamente.

import flet as ft
def main(page: ft.Page):
page.title = "Aggiungi elementi"
page.padding = 30
def aggiungi(e):
if input_nome.value:
# Aggiunge un nuovo testo alla lista
page.add(ft.Text(f"Ciao, {input_nome.value}!", color="blue"))
input_nome.value = "" # Svuota il campo
page.update()
input_nome = ft.TextField(label="Nome", hint_text="Inserisci un nome")
bottone = ft.ElevatedButton("Aggiungi", on_click=aggiungi)
page.add(input_nome, bottone)
ft.app(target=main)
def rimuovi_ultimo(e):
if page.controls: # Se ci sono componenti
page.controls.pop() # Rimuove l'ultimo
page.update()
def cancella_tutto(e):
page.controls.clear()
page.add(ft.Text("Lista svuotata!"))
page.update()

6. Esempio guidato: Lista della spesa dinamica

Section titled “6. Esempio guidato: Lista della spesa dinamica”

Mettiamo insieme tutto: eventi, stato, e page.controls:

import flet as ft
def main(page: ft.Page):
page.title = "Lista della Spesa"
page.padding = 30
page.scroll = ft.ScrollMode.AUTO
# Stato
elementi = [] # Lista degli elementi
# Titolo
page.add(
ft.Text("🛒 Lista della Spesa", size=28, weight="bold"),
ft.Text("Aggiungi gli elementi che ti servono", color="grey"),
ft.Divider(),
)
# Funzioni
def aggiungi(e):
if input_elemento.value and input_elemento.value.strip():
elemento = input_elemento.value.strip()
elementi.append(elemento)
# Aggiunge un testo alla UI
page.add(
ft.Container(
content=ft.Text(f"• {elemento}", size=16),
padding=ft.padding.symmetric(vertical=5, horizontal=10),
bgcolor="#f9f9f9",
border_radius=5,
)
)
input_elemento.value = ""
input_elemento.focus() # Riporta il focus sul campo
page.update()
def cancella_tutto(e):
elementi.clear()
# Ricostruisce la UI: tiene solo i primi 3 elementi
page.controls = page.controls[:3]
page.add(ft.Text("Lista svuotata!", color="red", italic=True))
page.update()
# Input e bottoni
input_elemento = ft.TextField(
label="Nuovo elemento",
hint_text="cosa ti serve?",
prefix_icon=ft.icons.SHOPPING_CART,
on_submit=aggiungi, # Invio come click
width=300,
)
page.add(
ft.Row([input_elemento, ft.ElevatedButton("Aggiungi", on_click=aggiungi)]),
ft.Row([
ft.OutlinedButton("Svuota lista", on_click=cancella_tutto, icon=ft.icons.DELETE),
]),
ft.Divider(),
)
ft.app(target=main)

ComponenteEventoQuando si attiva
ElevatedButtonon_clickClick del bottone
IconButtonon_clickClick dell’icona
TextButtonon_clickClick del testo
TextFieldon_changeL’utente digita
TextFieldon_submitL’utente preme Invio
Checkboxon_changeCambia lo stato
Switchon_changeCambia lo stato
Dropdownon_changeCambia selezione
Slideron_changeCambia valore

ConcettoCodiceSpiegazione
Evento baseon_click=mia_funzioneCollega un evento a una funzione
Parametro eventodef f(e):Riceve l’evento
Componente originee.controlIl componente che ha scatenato l’evento
Valore correntee.control.valueValore del componente
Aggiornamento UIpage.update()Ridisegna la pagina
Aggiungi componentepage.add(componente)Dopo l’avvio
Rimuovi componentepage.controls.pop()Rimuove l’ultimo
Stato con nonlocalnonlocal xModifica variabile esterna

🎯 Esercizio: “Convertitore di temperature”

Section titled “🎯 Esercizio: “Convertitore di temperature””

Crea un file convertitore.py che realizzi un convertitore Celsius ↔ Fahrenheit:

  1. Un titolo “Convertitore di Temperature”
  2. Un campo di input per inserire la temperatura
  3. Due bottoni:
    • ”→ Fahrenheit”: converte da Celsius a Fahrenheit
    • ”→ Celsius”: converte da Fahrenheit a Celsius
  4. Un testo che mostra il risultato della conversione
  5. Reset automatico: quando l’utente modifica il valore, il risultato precedente scompare
  • Celsius → Fahrenheit: F = (C × 9/5) + 32
  • Fahrenheit → Celsius: C = (F - 32) × 5/9
  • Usa float(input.value) per convertire il testo in numero
  • Gestisci l’errore se l’input non è un numero valido (usa try/except)
  • Per il reset automatico, usa on_change sul TextField
  • Arrotonda il risultato con round(valore, 1)
┌─────────────────────────────────┐
│ Convertitore di Temperature │
│ │
│ [Inserisci temperatura] │
│ │
│ [→ Fahrenheit] [→ Celsius] │
│ │
│ Risultato: 25°C = 77.0°F │
└─────────────────────────────────┘