Lezione 6: Navigazione — App multi-screen
Obiettivi
Section titled “Obiettivi”- Capire come funziona la navigazione in Flet
- Creare app con più schermate
- Passare dati tra una schermata e l’altra
- Usare NavigationBar e NavigationDrawer
1. Il concetto di route
Section titled “1. Il concetto di route”Le route (percorsi) permettono di avere più “pagine” nella stessa app, come in un sito web.
/home → Schermata Home/prodotti → Schermata Prodotti/profilo → Schermata ProfiloIn Flet, quando cambiamo route, la pagina si aggiorna mostrando contenuti diversi.
2. Navigazione base con page.go()
Section titled “2. Navigazione base con page.go()”Struttura fondamentale
Section titled “Struttura fondamentale”import flet as ft
def main(page: ft.Page): page.title = "App Multi-Screen" page.padding = 30
def naviga(destinazione): page.go(destinazione)
# Funzione chiamata quando la route cambia def route_change(route): page.views.clear() # Pulisce le viste precedenti
if page.route == "/": page.views.append( ft.View( route="/", controls=[ ft.Text("Home", size=30, weight="bold"), ft.ElevatedButton("Vai a Prodotti", on_click=lambda _: naviga("/prodotti")), ft.ElevatedButton("Vai a Contatti", on_click=lambda _: naviga("/contatti")), ] ) ) elif page.route == "/prodotti": page.views.append( ft.View( route="/prodotti", controls=[ ft.Text("Prodotti", size=30, weight="bold"), ft.Text("Elenco dei nostri prodotti..."), ft.ElevatedButton("← Indietro", on_click=lambda _: naviga("/")), ] ) ) elif page.route == "/contatti": page.views.append( ft.View( route="/contatti", controls=[ ft.Text("Contatti", size=30, weight="bold"), ft.Text("Scrivici a info@esempio.com"), ft.ElevatedButton("← Indietro", on_click=lambda _: naviga("/")), ] ) )
page.update()
page.on_route_change = route_change page.go("/") # Route iniziale
ft.app(target=main)3. ft.View: la schermata
Section titled “3. ft.View: la schermata”ft.View rappresenta una schermata completa. Proprietà principali:
| Proprietà | Descrizione |
|---|---|
route | Il nome della route (es. "/home") |
controls | Lista dei componenti nella schermata |
appbar | Barra superiore (opzionale) |
navigation_bar | Barra di navigazione inferiore (opzionale) |
horizontal_alignment | Allineamento orizzontale |
scroll | Scroll della schermata |
bgcolor | Colore di sfondo |
padding | Spazio dai bordi |
Versione semplificata con View diretta
Section titled “Versione semplificata con View diretta”page.views.append( ft.View( route="/dettaglio", controls=[ ft.Text("Dettaglio Prodotto", size=28, weight="bold"), ft.Text("Qui vedrai i dettagli..."), ft.ElevatedButton("← Torna indietro", on_click=lambda _: page.go("/")), ] ))4. AppBar (barra superiore)
Section titled “4. AppBar (barra superiore)”ft.AppBar aggiunge una barra in cima alla schermata:
ft.View( route="/", appbar=ft.AppBar( title=ft.Text("Home"), bgcolor="blue", color="white", leading=ft.Icon(ft.icons.HOME), # Icona a sinistra actions=[ # Azioni a destra ft.IconButton(ft.icons.SETTINGS, on_click=lambda _: print("Settings")), ft.IconButton(ft.icons.NOTIFICATIONS, on_click=lambda _: print("Notifiche")), ], ), controls=[ ft.Text("Contenuto della Home", size=20), ])Proprietà AppBar
Section titled “Proprietà AppBar”| Proprietà | Descrizione |
|---|---|
title | Titolo centrale (ft.Text) |
bgcolor | Colore sfondo |
color | Colore testo/icone |
leading | Widget a sinistra (es. icona menu) |
actions | Lista di widget a destra |
center_title | Centra il titolo (True/False) |
5. Esempio guidato: App con navigazione completa
Section titled “5. Esempio guidato: App con navigazione completa”import flet as ft
def main(page: ft.Page): page.title = "My App" page.theme_mode = ft.ThemeMode.LIGHT
def naviga(e, destinazione): page.go(destinazione)
def route_change(route): page.views.clear()
# === HOME === if page.route == "/": page.views.append( ft.View( route="/", appbar=ft.AppBar( title=ft.Text("Home"), bgcolor=ft.colors.BLUE, color=ft.colors.WHITE, center_title=True, ), controls=[ ft.Container( content=ft.Column([ ft.Text("🏠 Benvenuto!", size=32, weight="bold"), ft.Text("Questa è la schermata principale.", size=16, color="grey"), ft.Divider(height=30), ft.ElevatedButton("📦 Vedi Prodotti", on_click=lambda e: naviga(e, "/prodotti"), width=250), ft.ElevatedButton("📞 Contatti", on_click=lambda e: naviga(e, "/contatti"), width=250), ft.ElevatedButton("ℹ️ Info App", on_click=lambda e: naviga(e, "/info"), width=250), ], alignment=ft.MainAxisAlignment.CENTER), padding=30, ) ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, ) )
# === PRODOTTI === elif page.route == "/prodotti": page.views.append( ft.View( route="/prodotti", appbar=ft.AppBar( title=ft.Text("Prodotti"), bgcolor=ft.colors.BLUE, color=ft.colors.WHITE, leading=ft.IconButton(ft.icons.ARROW_BACK, on_click=lambda _: page.go("/")), ), controls=[ ft.ListView(spacing=10, padding=20, expand=True, auto_scroll=False), ], ) ) # Aggiunge prodotti alla lista prodotti = ["Laptop", "Smartphone", "Tablet", "Cuffie", "Mouse"] for p in prodotti: page.views[-1].controls[0].controls.append( ft.Container( content=ft.Row([ ft.Icon(ft.icons.ELECTRONICS, color="blue"), ft.Column([ ft.Text(p, weight="bold", size=16), ft.Text("Prodotto disponibile", color="green", size=12), ]), ft.Icon(ft.icons.CHEVRON_RIGHT, color="grey"), ]), padding=15, bgcolor="white", border_radius=10, shadow=ft.BoxShadow(blur_radius=5, color=ft.colors.GREY_300), ) )
# === CONTATTI === elif page.route == "/contatti": page.views.append( ft.View( route="/contatti", appbar=ft.AppBar( title=ft.Text("Contatti"), bgcolor=ft.colors.BLUE, color=ft.colors.WHITE, leading=ft.IconButton(ft.icons.ARROW_BACK, on_click=lambda _: page.go("/")), ), controls=[ ft.Column([ ft.ListTile( leading=ft.Icon(ft.icons.EMAIL), title=ft.Text("Email"), subtitle=ft.Text("info@esempio.com"), ), ft.ListTile( leading=ft.Icon(ft.icons.PHONE), title=ft.Text("Telefono"), subtitle=ft.Text("+39 123 456 7890"), ), ft.ListTile( leading=ft.Icon(ft.icons.LOCATION_ON), title=ft.Text("Indirizzo"), subtitle=ft.Text("Via Roma 1, Milano"), ), ], spacing=5), ] ) )
elif page.route == "/info": page.views.append( ft.View( route="/info", appbar=ft.AppBar( title=ft.Text("Info App"), bgcolor=ft.colors.BLUE, color=ft.colors.WHITE, leading=ft.IconButton(ft.icons.ARROW_BACK, on_click=lambda _: page.go("/")), ), controls=[ ft.Column([ ft.Text("My App v1.0", size=24, weight="bold"), ft.Text("Creata con Flet e Python", color="grey"), ft.Text("Anno 2025-2026", color="grey"), ], alignment=ft.MainAxisAlignment.CENTER, horizontal_alignment=ft.CrossAxisAlignment.CENTER), ], horizontal_alignment=ft.CrossAxisAlignment.CENTER, vertical_alignment=ft.MainAxisAlignment.CENTER, ) )
page.update()
page.on_route_change = route_change page.go("/")
ft.app(target=main)6. Tecnica: pagina “Back” con pop
Section titled “6. Tecnica: pagina “Back” con pop”Invece di ricostruire la view, possiamo usare page.views.pop() per tornare indietro:
def route_change(route): # Se stiamo navigando all'indietro (già nella pila) # non serve ricostruire ...
# Bottone Back che fa pop:ft.IconButton( ft.icons.ARROW_BACK, on_click=lambda _: page.views.pop() or page.update())7. Tabella riassuntiva
Section titled “7. Tabella riassuntiva”| Comando | Descrizione |
|---|---|
page.go("/route") | Naviga a una route |
page.views | Pila delle viste (lista) |
page.views.clear() | Pulisce tutte le viste |
page.views.append(ft.View(...)) | Aggiunge una nuova vista |
page.views.pop() | Torna alla vista precedente |
page.on_route_change | Funzione chiamata al cambio route |
page.route | Route corrente |
ft.View(route="/", controls=[...]) | Crea una nuova schermata |
ft.AppBar(title=..., bgcolor=...) | Barra superiore |
8. Esercizio autonomo
Section titled “8. Esercizio autonomo”🎯 Esercizio: “App Ricette”
Section titled “🎯 Esercizio: “App Ricette””Crea un file ricette.py che realizzi un’app di ricette con navigazione:
Schermata Home (/):
- Titolo ”🍳 Le Mie Ricette”
- Griglia (GridView) con almeno 4 ricette (usa nomi e icone)
- Ogni card è cliccabile → naviga alla schermata dettaglio
Schermata Dettaglio (/ricetta/1, /ricetta/2, …):
- Mostra il nome della ricetta (grande, grassetto)
- Mostra ingredienti (usa una lista con bullet)
- Mostra procedimento (paragrafo)
- Un bottone ”← Torna alle ricette”
Requisiti tecnici:
- Usa
ft.Viewper ogni schermata - Usa
ft.AppBarcon titolo e back button - Usa
page.on_route_changeper gestire la navigazione - Le card sulla home devono avere ombra e angoli arrotondati
Suggerimenti
Section titled “Suggerimenti”- Per passare l’ID ricetta: usa route tipo
"/ricetta/1"e poi analizzapage.routecon.split("/") - Per le card cliccabili: usa
ft.Container(on_click=...)o avvolgi inft.GestureDetector - Usa
ft.ListTileper un layout pulito degli ingredienti