Skip to Content
FlutterLezione 2 Widget Di Base2.3 Struttura di base di un'app Flutter con i widget Material

Struttura di base di un’app Flutter

Obiettivo

Capire e sperimentare la struttura fondamentale di una app Flutter utilizzando i widget MaterialApp, Scaffold, AppBar, NavigationBar, FloatingActionButton e Drawer.


1. MaterialApp

Descrizione

MaterialApp è il widget principale che configura l’intera app in stile Material Design. Offre gestione del tema, routing, localizzazione, transizioni, e molto altro. È la base per creare app moderne e coerenti con le linee guida di Google.

Gestione del tema

MaterialApp permette di definire:

  • un theme (chiaro)
  • un darkTheme (scuro)
  • un themeMode (per scegliere quale usare)

Puoi anche usare ColorScheme.fromSeed per generare automaticamente una palette Material 3 da un solo colore base.

Esempio classico (Material 2):
MaterialApp( title: 'La mia prima app', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(), debugShowCheckedModeBanner: false, )
Esempio moderno (Material 3 con seed):
MaterialApp( title: 'App con tema generato', theme: ThemeData.from( colorScheme: ColorScheme.fromSeed(seedColor: Colors.teal), useMaterial3: true, ), darkTheme: ThemeData.from( colorScheme: ColorScheme.fromSeed( seedColor: Colors.teal, brightness: Brightness.dark, ), useMaterial3: true, ), themeMode: ThemeMode.system, home: MyHomePage(), )

Attributi utili

  • title: Titolo visibile nel task switcher.
  • theme: Tema principale dell’app (colori, stili).
  • darkTheme: Tema per modalità scura.
  • themeMode: Selettore tra chiaro, scuro o automatico.
  • home: Widget di partenza dell’app.
  • routes: Mappa delle rotte nominate.
  • initialRoute: Rotta iniziale.
  • debugShowCheckedModeBanner: Mostra o nasconde il banner DEBUG.

Alternative a MaterialApp

Anche se MaterialApp è lo standard per creare applicazioni Flutter in stile Material Design, non è l’unica opzione disponibile. Flutter offre altre strutture base per casi d’uso diversi:

1. CupertinoApp

Usata per creare app in stile iOS, con supporto a widget Cupertino.

CupertinoApp( home: CupertinoPageScaffold( navigationBar: CupertinoNavigationBar( middle: Text('Titolo iOS'), ), child: Center(child: Text('Contenuto')), ), )
2. WidgetsApp

È la base comune tra MaterialApp e CupertinoApp. Non fornisce widget grafici predefiniti, ma gestisce routing, localizzazione e navigazione.

WidgetsApp( color: Colors.white, builder: (context, child) => MyCustomApp(child: child), home: MyHomePage(), )
3. App minimal senza struttura predefinita

Per esperimenti, librerie o ambienti altamente personalizzati:

void main() { runApp( Directionality( textDirection: TextDirection.ltr, child: Center(child: Text('App base senza MaterialApp')), ), ); }

App multipiattaforma: approccio consigliato

Per creare un’app Flutter multipiattaforma (Android e iOS) coerente con il sistema operativo, ci sono due approcci principali:

Approccio MaterialApp + widget adattivi
  • Usa MaterialApp con useMaterial3: true
  • Usa widget adattivi come Switch.adaptive, Scrollbar.adaptive, ecc.
  • Personalizza il layout o stile usando Platform.isIOS dove serve

Esempio:

MaterialApp( theme: ThemeData( colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo), useMaterial3: true, ), home: Platform.isIOS ? CupertinoStyledPage() : MaterialStyledPage(), )
Confronto rapido degli approcci
ObiettivoMiglior approccio
App coerente su entrambe le piattaformeMaterialApp con Material 3 + widget adattivi
App con stile nativo per ogni OSlogica Platform.isIOS
Sviluppo rapido e codice unificatoMaterialApp + layout responsivo

Quando usare un’alternativa

  • Vuoi replicare lo stile nativo iOS → usa CupertinoApp
  • Vuoi una base pulita e neutra per widget personalizzati → usa WidgetsApp
  • Stai creando un widget standalone o una libreria → usa solo ciò che serve, senza Material

Attributi utili

  • title: Titolo visibile nel task switcher.
  • theme: Tema principale dell’app (colori, stili).
  • home: Widget di partenza dell’app.
  • routes: Mappa delle rotte nominate.
  • initialRoute: Rotta iniziale.
  • debugShowCheckedModeBanner: Mostra o nasconde il banner DEBUG.
MaterialApp( title: 'La mia prima app', theme: ThemeData(primarySwatch: Colors.blue), home: MyHomePage(), debugShowCheckedModeBanner: false, )

2. Scaffold

Descrizione

Scaffold è uno dei widget più importanti in Flutter perché fornisce la struttura visiva e logica di una schermata. Viene usato come contenitore principale di ogni “pagina” o vista dell’app. Integra automaticamente molti altri widget chiave del Material Design, come:

  • AppBar: la barra superiore
  • Drawer: il menu laterale
  • FloatingActionButton: pulsante d’azione principale
  • BottomNavigationBar: barra di navigazione in basso
  • SnackBar, BottomSheet, FAB, ecc.

Scaffold si adatta bene al contesto (es. mostra correttamente il Drawer o il FAB) e gestisce automaticamente spazi come lo SafeArea, il rientro sotto tastiere o notch.

Attributi utili

  • appBar: Una AppBar da visualizzare in alto.
  • body: Contenuto principale della schermata.
  • drawer: Menu laterale a scomparsa.
  • bottomNavigationBar: Barra inferiore di navigazione, supporta BottomNavigationBar e NavigationBar.
  • floatingActionButton: Pulsante d’azione fluttuante.
  • backgroundColor: Colore dello sfondo.

Esempio

Scaffold( appBar: AppBar(title: Text('Home')), body: Center(child: Text('Contenuto principale')), bottomNavigationBar: NavigationBar( selectedIndex: 0, onDestinationSelected: (index) => print('Selezionato: \$index'), destinations: [ NavigationDestination(icon: Icon(Icons.home), label: 'Home'), NavigationDestination(icon: Icon(Icons.settings), label: 'Impostazioni'), ], ), floatingActionButton: FloatingActionButton( onPressed: () {}, child: Icon(Icons.add), ), )

3. AppBar

Descrizione

AppBar è una barra nella parte superiore dello schermo che può contenere un titolo, pulsanti di azione, icone e menu.

Attributi utili

  • title: Un widget, tipicamente Text, che rappresenta il titolo.
  • leading: Icona o widget visualizzato a sinistra (es. hamburger menu).
  • actions: Lista di widget (tipicamente IconButton) visualizzati a destra.
  • backgroundColor: Colore dello sfondo.
  • elevation: Ombra sotto l’app bar.
  • centerTitle: Per centrare il title true/false.

Esempio

AppBar( title: Text('Titolo'), actions: [ IconButton(icon: Icon(Icons.search), onPressed: () {}) ], )

4. BottomNavigationBar (NavigationBar)

Descrizione

BottomNavigationBar consente la navigazione tra diverse schermate principali di un’app. Si trova in basso e visualizza icone e testi.

Attributi utili

  • items: Lista di elementi (BottomNavigationBarItem).
  • currentIndex: Indice attualmente selezionato.
  • onTap: Funzione callback al tap su un item.
  • type: Tipo di visualizzazione (fixed o shifting).
  • selectedItemColor: Colore dell’elemento selezionato.

Esempio

BottomNavigationBar( currentIndex: 0, onTap: (index) => print('Hai premuto la voce $index'), items: [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Impostazioni'), ], )

5. FloatingActionButton

Descrizione

Un bottone rotondo che “fluttua” sopra il layout. Usato spesso per azioni principali (es. aggiungere, scrivere, creare).

Attributi utili

  • onPressed: Funzione da eseguire al tap.
  • child: Contenuto interno (di solito Icon).
  • backgroundColor: Colore di sfondo.
  • tooltip: Testo mostrato come tooltip.
  • heroTag: Per transizioni animate.

Esempio

FloatingActionButton( onPressed: () => print('Premuto'), child: Icon(Icons.add), tooltip: 'Aggiungi', )

6. Drawer

Descrizione

Drawer è un menu laterale che appare scorrendo o toccando l’icona del menu. Utile per la navigazione secondaria o opzioni generali.

Attributi utili

  • child: Contenuto del Drawer (tipicamente ListView).
  • elevation: Altezza dell’ombra.
  • backgroundColor: Colore dello sfondo.
  • width: Larghezza del menu.

Esempio

Drawer( child: ListView(// o a Column children: [ DrawerHeader(child: Text('Menu')), ListTile(title: Text('Home'), onTap: () {}), ListTile(title: Text('Profilo'), onTap: () {}), ], ), )

7. PageView

Descrizione

Un PageView consente di scorrere tra una serie di widget come se fossero pagine. È utile per creare interfacce utente in stile onboarding, gallerie di immagini o contenuti multipli in sequenza.

Attributi utili

  • controller: Gestisce il controllo del movimento tra le pagine.
  • children: Un elenco di widget che compongono le pagine.
  • scrollDirection: Direzione dello scorrimento (orizzontale o verticale).
  • onPageChanged: Callback che viene chiamato quando la pagina cambia.

Esempio

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: PageViewExample(), ); } } class PageViewExample extends StatelessWidget { final PageController _controller = PageController(); @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('PageView Example'), ), body: PageView( controller: _controller, children: [ Container(color: Colors.red, child: Center(child: Text('Pagina 1'))), Container(color: Colors.green, child: Center(child: Text('Pagina 2'))), Container(color: Colors.blue, child: Center(child: Text('Pagina 3'))), ], ), ); } }

Esempio - Onboarding App

import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: OnboardingPage(), ); } } class OnboardingPage extends StatefulWidget { @override _OnboardingPageState createState() => _OnboardingPageState(); } class _OnboardingPageState extends State<OnboardingPage> { final PageController _pageController = PageController(); int _currentPage = 0; void _nextPage() { if (_currentPage < 2) { _pageController.nextPage( duration: Duration(milliseconds: 300), curve: Curves.easeInOut, ); } } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ PageView( controller: _pageController, onPageChanged: (index) { setState(() { _currentPage = index; }); }, children: [ _buildPage( color: Colors.red, text: 'Benvenuto in MyApp!', image: Icons.ac_unit, ), _buildPage( color: Colors.green, text: 'Organizza le tue attività.', image: Icons.assignment, ), _buildPage( color: Colors.blue, text: 'Inizia ora!', image: Icons.arrow_forward, ), ], ), Positioned( bottom: 20, left: 0, right: 0, child: Center( child: ElevatedButton( onPressed: _nextPage, child: Text(_currentPage == 2 ? 'Inizia' : 'Avanti'), ), ), ), ], ), ); } Widget _buildPage({required Color color, required String text, required IconData image}) { return Container( color: color, child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(image, size: 100, color: Colors.white), SizedBox(height: 20), Text( text, style: TextStyle(fontSize: 24, color: Colors.white), ), ], ), ), ); } }

Note

È possibile utilizzare PageController per controllare il movimento tra le pagine e monitorare l’indice corrente.


8. Snackbar

Descrizione

Una Snackbar è un piccolo messaggio temporaneo che appare nella parte inferiore dello schermo. Serve per fornire feedback immediato all’utente dopo un’azione, come l’invio di un form o la rimozione di un elemento.

Attributi utili

  • content: Il widget (tipicamente un Text) mostrato nella snackbar.
  • duration: Durata della visualizzazione.
  • action: Aggiunge un bottone (es. “Annulla”).
  • backgroundColor: Colore di sfondo della snackbar.
  • behavior: Controlla la posizione (es. SnackBarBehavior.floating).

Esempio

ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Elemento salvato'), duration: Duration(seconds: 2), action: SnackBarAction( label: 'Annulla', onPressed: () { // codice per annullare }, ), ), );
Note

A partire da Flutter 2.0, ScaffoldMessenger.of(context) è il modo corretto per mostrare una snackbar, al posto di Scaffold.of(context).


9. Dialog

Descrizione

showDialog è una funzione che apre un modale centrato sopra il contenuto corrente. È utile per conferme, avvisi, messaggi brevi o azioni rapide, senza lasciare la schermata attuale.

Attributi utili di showDialog

  • context: Contesto da cui viene chiamato.
  • builder: Funzione che restituisce il widget da mostrare (tipicamente AlertDialog o Dialog).
  • barrierDismissible: Se true, l’utente può chiudere il dialog toccando fuori dal box (default: true).
  • barrierColor: Colore dell’overlay dietro il dialog.
  • useSafeArea: Se true, evita aree protette (notch, status bar).
  • useRootNavigator: Se true, usa il Navigator di livello più alto.

Variante base: AlertDialog

showDialog( context: context, builder: (context) { return AlertDialog( title: Text('Attenzione'), content: Text('Sei sicuro di voler continuare?'), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Annulla'), ), TextButton( onPressed: () { // azione confermata Navigator.pop(context); }, child: Text('Conferma'), ), ], ); }, );

Variante personalizzata: Dialog

showDialog( context: context, builder: (_) => Dialog( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(20), ), child: Padding( padding: const EdgeInsets.all(16), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text('Messaggio personalizzato'), SizedBox(height: 20), ElevatedButton( onPressed: () => Navigator.pop(context), child: Text('Chiudi'), ), ], ), ), ), );

Variante con SimpleDialog

showDialog( context: context, builder: (context) { return SimpleDialog( title: Text('Seleziona un’opzione'), children: [ SimpleDialogOption( onPressed: () => Navigator.pop(context, 'A'), child: Text('Opzione A'), ), SimpleDialogOption( onPressed: () => Navigator.pop(context, 'B'), child: Text('Opzione B'), ), ], ); }, );

Ricevere un valore dal dialog

final risultato = await showDialog<String>( context: context, builder: (_) => AlertDialog( content: Text('Scegli un’opzione'), actions: [ TextButton( onPressed: () => Navigator.pop(context, 'Accetta'), child: Text('Accetta'), ), TextButton( onPressed: () => Navigator.pop(context, 'Rifiuta'), child: Text('Rifiuta'), ), ], ), ); print('Hai scelto: $risultato');

Altre varianti utili

VarianteDescrizione
AlertDialogLayout standard con titolo, testo e azioni
SimpleDialogPer selezione rapida tra più opzioni
DialogTotalmente personalizzabile
showGeneralDialogPer creare dialog con animazioni personalizzate

💡
Tip

I dialog sono modali e vanno chiusi sempre con Navigator.pop(context), eventualmente passando un valore di ritorno.

Lo incontreremo nuovamente durante la lezione sulla navigazione

Esercizio Pratico

Prima prova pratica, avrete la possibilità di applicare tutto ciò che fino a ora abbiamo esplorato. L’esercitazione si baserà nello sviluppare un applicazione a singola pagina che rappresenti la pagina di un prodotto all’interno di un E-commerce.