Skip to Content
FlutterLezione 2 Widget Di Base2.2 - Primi widget di Flutter

2.2: Primi widget di Flutter

Layout

Come possiamo realizzare un primo layout per la nostra applicazione?

Vediamo i principali Widget per gestire il layout


Container

Il Container è un widget contenitore molto flessibile.

🔹 Attributi utili

AttributoDescrizione
width, heightImposta dimensioni fisse
colorColore di sfondo
alignmentPosiziona il contenuto (Alignment.center, ecc.)
paddingSpazio interno (usa EdgeInsets)
marginSpazio esterno
decorationPer bordi, sfondi, gradienti
childIl contenuto del container
Container( width: 150, height: 100, padding: EdgeInsets.all(12), margin: EdgeInsets.only(top: 20), alignment: Alignment.center, decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(12), border: Border.all(color: Colors.black, width: 2), boxShadow: [ BoxShadow( color: Colors.black26, blurRadius: 6, offset: Offset(2, 2), ) ], ), child: Text("Decorato"), )

Center

Il Center serve a centrare un widget rispetto al suo genitore.

🔹 Attributi utili

AttributoDescrizione
childIl widget da centrare
heightFactor, widthFactorMoltiplicatore sul contenuto
Center( child: Text("Centrato!"), )

Padding

Descrizione

Il widget Padding serve per aggiungere dello spazio interno (padding) intorno a un altro widget. Può essere usato ovunque per migliorare la leggibilità, separare elementi, o allineare correttamente i contenuti nella UI.

È un contenitore invisibile che avvolge un widget figlio e gli fornisce margine interno.


Struttura di base

Padding( padding: EdgeInsets.all(16.0), child: Text("Ciao mondo!"), )

Attributi utili

AttributoTipoDescrizione
paddingEdgeInsetsSpazio interno da applicare (obbligatorio)
childWidgetIl widget contenuto nel padding

Come si definisce EdgeInsets?

MetodoSignificato
EdgeInsets.all(8)Padding uguale su tutti i lati
EdgeInsets.symmetric(h, v)Padding orizzontale/verticale
EdgeInsets.only(...)Padding specifico per top, bottom, left, right

📌 Esempio:

Padding( padding: EdgeInsets.only(left: 12, top: 8), child: Icon(Icons.star), )

Esercizi utili

  1. Avvolgi 3 Text con padding diversi (uno solo a sinistra, uno simmetrico, uno tutto attorno) e osserva come cambia il layout.
  2. Prova l’effetto del padding mettendo un colore di sfondo al figlio

SizedBox

Descrizione

Il widget SizedBox serve per inserire uno spazio fisso o impostare una dimensione specifica a un widget figlio.
È uno dei widget più semplici ma più utili per controllare layout e spacing.

Funziona anche senza figlio, per creare spazi vuoti.

Struttura di base

SizedBox( width: 100, height: 50, child: Text("Contenuto"), )

Attributi utili

AttributoTipoDescrizione
widthdouble?Larghezza del box
heightdouble?Altezza del box
childWidget?Il widget da visualizzare nelle dimensioni forzate

Uso come spazio vuoto

SizedBox(height: 20)

Molto usato in Column o Row per aggiungere spazio tra widget.


Esempio pratico

Column( children: [ Text("Titolo"), SizedBox(height: 16), Text("Descrizione"), ], )

Esercizio utile

“Inserisci 3 Text in una colonna. Usa SizedBox(height: 32) per distanziare i primi due, e SizedBox(height: 8) tra i secondi due. Osserva la differenza visiva.”


Widget: Spacer

Descrizione

Il widget Spacer è usato all’interno di Row, Column o Flex per occupare lo spazio disponibile in modo flessibile.
Funziona come uno “spazio vuoto espandibile” che cresce in base allo spazio residuo e al valore del suo flex.

Utile per spingere o distribuire elementi lungo l’asse principale di un layout flessibile.

Struttura di base

Row( children: [ Icon(Icons.star), Spacer(), Icon(Icons.settings), ], )

Attributi utili

AttributoTipoDescrizione
flexintValore di flessibilità: maggiore = occupa più spazio

Differenza con SizedBox

  • Spacer() occupa tutto lo spazio disponibile dinamicamente
  • SizedBox(width: 20) occupa uno spazio fisso e definito

Esempio pratico

Row( children: [ Text("Indietro"), Spacer(), Text("Avanti"), ], )

In questo esempio, Spacer() spinge i due Text agli estremi del Row.

Esercizio utile

“In una Row, inserisci tre icone. Usa due Spacer() per distribuire le icone in modo uniforme. Prova a cambiare i valori di flex.”


ConstrainedBox

Limita un widget a delle dimensioni minime e/o massime.

🔹 Attributi utili

AttributoDescrizione
constraintsSpecifica i limiti (usa BoxConstraints)
childIl widget da vincolare
ConstrainedBox( constraints: BoxConstraints( minWidth: 100, maxWidth: 200, minHeight: 50, ), child: Container(color: Colors.amber), )

📝 Esercitazione 1: La tua prima “Card”

Obiettivo

Creare una card centrata nello schermo che abbia:

  • Un contenitore con sfondo colorato
  • Bordi arrotondati
  • Un po’ di padding interno
  • Un testo al centro
  • Una Size massima (con ConstrainedBox)
  • Un’ombra (usando boxShadow)
  • Un colore di sfondo a tua scelta

Specifiche tecniche

  • Usa Center per centrare
  • Usa ConstrainedBox per limitare la larghezza e l’altezza a max 300px
  • Usa Container con:
  • padding
  • decoration con BoxDecoration
  • Text centrato all’interno
  • Colori e stile a tua scelta
Note

🕓 Tempo stimato 10–15 minuti


Layout Multipli e Scorribili in Flutter

Impariamo a usare alcuni dei widget di layout più importanti per costruire interfacce complesse e scorribili in Flutter.

🔹 Column

Un Column organizza i widget in verticale, uno sopra l’altro.

Column( children: [ Text("Titolo"), ElevatedButton(onPressed: () {}, child: Text("Azione")), ], )

Attributi utili

AttributoDescrizioneValori disponibili
childrenLista di widgetQualsiasi lista di widget
mainAxisAlignmentAllinea i figli lungo l’asse verticalestart, center, end, spaceBetween, spaceAround, spaceEvenly
crossAxisAlignmentAllinea lungo l’asse orizzontalestart, center, end, stretch, baseline
mainAxisSizeSpazio occupato dal ColumnMainAxisSize.max (default), MainAxisSize.min
textDirectionDirezione del testo (raro)TextDirection.ltr, TextDirection.rtl
verticalDirectionDirezione di crescita verticaleVerticalDirection.down (default), VerticalDirection.up

🔹 Row

Un Row organizza i widget in orizzontale, affiancati.

Row( children: [ Icon(Icons.star), Text("Preferito"), ], )

Attributi utili

AttributoDescrizioneValori disponibili
childrenLista di widgetQualsiasi lista di widget
mainAxisAlignmentSpazio tra i figlistart, center, end, spaceBetween, spaceAround, spaceEvenly
crossAxisAlignmentAllinea verticalmentestart, center, end, stretch, baseline
mainAxisSizeSpazio occupato dalla RowMainAxisSize.max, MainAxisSize.min
textDirectionOrdine dei widgetTextDirection.ltr, TextDirection.rtl
verticalDirectionDirezione verticale (per crossAxis)VerticalDirection.down, VerticalDirection.up

🔹 Stack

Un Stack sovrappone i widget uno sull’altro (come livelli).

Stack( children: [ Image.asset("bg.jpg"), Positioned( bottom: 10, left: 10, child: Text("Testo sopra immagine"), ) ], )

Attributi utili

AttributoDescrizioneValori disponibili
alignmentAllineamento predefinito dei figliAlignment.center, topLeft, bottomRight, ecc.
fitCome i figli si adattano allo StackStackFit.loose (default), StackFit.expand, StackFit.passthrough
clipBehaviorCome viene ritagliato l’overflow dei figliClip.none, Clip.hardEdge, Clip.antiAlias, ecc.
childrenLista di widget sovrappostiQualsiasi lista di widget

🔹 Widget speciale: Positioned

Serve per posizionare un figlio dentro uno Stack.

Positioned( top: 10, left: 20, child: Text("Posizionato"), )

Attributi utili

AttributoDescrizioneValori
topDistanza dal bordo superioredouble
leftDistanza dal bordo sinistrodouble
rightDistanza dal bordo destrodouble
bottomDistanza dal bordo inferioredouble
childWidget da posizionareWidget

🔹 ListView

ListView è un elenco scorrevole verticalmente.

ListView( children: [ ListTile(title: Text("Elemento 1")), ListTile(title: Text("Elemento 2")), ], )

Varianti

  • ListView.builder → per liste dinamiche e ottimizzate

Widget Utile

  • ListTile → widget standard per voci di lista

🔹 GridView

GridView mostra i contenuti in forma di griglia, come una tabella.

GridView.count( crossAxisCount: 2, children: [ Container(color: Colors.blue), Container(color: Colors.green), ], )

Varianti

  • GridView.count → facile da usare con numero fisso di colonne
  • GridView.builder → per griglie dinamiche e performanti

🔹 Expanded

Espande un widget per occupare tutto lo spazio disponibile in Row o Column.

Attributi utili

AttributoDescrizione
flexPeso del widget rispetto agli altri
childIl widget da espandere
Row( children: [ Expanded( flex: 2, child: Container(color: Colors.red), ), Expanded( flex: 1, child: Container(color: Colors.green), ), ], )

Esercizio 2: Layout base con Row, Column, Stack e Container

Obiettivo

Creare una schermata con layout semplici utilizzando:

  • Column e Row per disporre elementi
  • Stack per sovrapporre contenuti
  • Container per stile, colore e dimensioni

Cosa deve contenere

  1. Una Column con 3 Container colorati
  2. Una Row con 2 box affiancati
  3. Uno Stack con un Container di sfondo e un Text sovrapposto

Widget per il contenuto

Text

Image

In Flutter possiamo inserire immagini all’interno delle nostre schermate importando il file all’interno del progetto oppure specificando l’url per recuperarla da un server.

Abbiamo due costruttori a disposizione

  • Image.network
  • Image.asset

Attributi utili

AttributoDescrizione
heightSettare un altezza
widthSettare una larghezza
fitDetermina come l’immagine si adatta allo spazio disponibile all’interno del contenitore
alignmentDefinisce come viene posizionata l’immagine all’interno del contenitore, soprattutto se l’immagine è più piccola dello spazio disponibile (es. in BoxFit.none o scaleDown)
colorSe non è null, il colore fornito viene sovrapposto all’immagine con l’effetto specificato in colorBlendMode
colorBlendModeSpecifica come combinare il colore (color) con l’immagine

Image.network

Per l’esercitazione utilizzeremo il servizio online PicsumPhotos Otteniamo un immagine random attraverso il link https://picsum.photos/500/300

Image.network( 'https://picsum.photos/500/300', height:100, ... )

Image.asset

Per salvare l’immagine nel pacchetto apk possiamo procedere così:

  1. Creare una directory assets/images nella directory principale
  2. Inserire la voce assets: su pubspec sotto la voce flutter:
  3. Inserire sotto la voce assets: tutte le immagini o directory, che vogliamo possano essere raggiunte nel progetto, con un trattino - assets/images/ oppure - assets/images/image.png per limitare l’accesso all’immagine
Image.asset( 'assets/images/image.png', height:100, ... )

Tool per capire come gli attributi modificano l’immagine

import 'package:flutter/material.dart'; void main() { runApp(ImageFitAlignmentDemo()); } class ImageFitAlignmentDemo extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( appBar: AppBar(title: Text('Image Fit & Alignment Explorer')), body: ImageFitExplorer(), ), ); } } class ImageFitExplorer extends StatefulWidget { @override _ImageFitExplorerState createState() => _ImageFitExplorerState(); } class _ImageFitExplorerState extends State<ImageFitExplorer> { BoxFit _fit = BoxFit.contain; Alignment _alignment = Alignment.center; BlendMode _blendMode = BlendMode.srcOver; bool _useColor = false; final Map<BoxFit, String> fitDescriptions = { BoxFit.fill: "Riempi tutto lo spazio distorcendo l'immagine se necessario.", BoxFit.contain: "L'immagine si adatta interamente mantenendo le proporzioni.", BoxFit.cover: "Copre tutto lo spazio ritagliando l'immagine se serve.", BoxFit.fitWidth: "Si adatta alla larghezza mantenendo le proporzioni.", BoxFit.fitHeight: "Si adatta all'altezza mantenendo le proporzioni.", BoxFit.none: "L'immagine resta della dimensione originale (non scala).", BoxFit.scaleDown: "Come 'none', ma si riduce se è troppo grande.", }; final Map<Alignment, String> alignmentDescriptions = { Alignment.topLeft: "Allinea in alto a sinistra.", Alignment.topCenter: "Allinea in alto al centro.", Alignment.topRight: "Allinea in alto a destra.", Alignment.centerLeft: "Allinea al centro sinistra.", Alignment.center: "Allinea perfettamente al centro.", Alignment.centerRight: "Allinea al centro destra.", Alignment.bottomLeft: "Allinea in basso a sinistra.", Alignment.bottomCenter: "Allinea in basso al centro.", Alignment.bottomRight: "Allinea in basso a destra.", }; final Map<BlendMode, String> blendModeDescriptions = { BlendMode.clear: "Rende trasparente la parte coperta.", BlendMode.src: "Usa solo il colore sorgente.", BlendMode.dst: "Mantiene solo il colore di sfondo.", BlendMode.srcOver: "Sovrappone il colore sorgente al colore di sfondo (default).", BlendMode.dstOver: "Sovrappone il colore di sfondo al sorgente.", BlendMode.srcIn: "Mostra solo le parti dove sorgente e sfondo si sovrappongono.", BlendMode.dstIn: "Mantiene lo sfondo solo dove c'è il sorgente.", BlendMode.srcOut: "Mostra sorgente solo dove non c'è sfondo.", BlendMode.dstOut: "Mantiene sfondo solo dove non c'è sorgente.", BlendMode.srcATop: "Mostra il sorgente sopra lo sfondo, solo nelle aree comuni.", BlendMode.dstATop: "Mostra sfondo sopra il sorgente, solo nelle aree comuni.", BlendMode.xor: "Unisce sorgente e sfondo escludendo le aree comuni.", BlendMode.plus: "Somma i colori.", BlendMode.modulate: "Moltiplica i colori tra loro.", BlendMode.screen: "Effetto schermo (inverso del multiply).", BlendMode.overlay: "Combinazione tra multiply e screen.", BlendMode.darken: "Tiene il colore più scuro tra sorgente e sfondo.", BlendMode.lighten: "Tiene il colore più chiaro.", BlendMode.colorDodge: "Schiarisce lo sfondo per riflettere il colore del sorgente.", BlendMode.colorBurn: "Scurisce lo sfondo per riflettere il colore del sorgente.", BlendMode.hardLight: "Combina overlay e multiply a seconda del colore.", BlendMode.softLight: "Effetto luce morbida.", BlendMode.difference: "Differenza assoluta tra i colori.", BlendMode.exclusion: "Simile a difference ma meno estremo.", BlendMode.multiply: "Moltiplica i colori tra sorgente e sfondo.", BlendMode.hue: "Applica tonalità del sorgente al colore di sfondo.", BlendMode.saturation: "Applica saturazione del sorgente.", BlendMode.color: "Applica colore del sorgente, mantenendo luminosità.", BlendMode.luminosity: "Applica luminosità del sorgente al colore di sfondo.", }; final List<BoxFit> fitOptions = BoxFit.values; final List<Alignment> alignmentOptions = [ Alignment.topLeft, Alignment.topCenter, Alignment.topRight, Alignment.centerLeft, Alignment.center, Alignment.centerRight, Alignment.bottomLeft, Alignment.bottomCenter, Alignment.bottomRight, ]; final List<BlendMode> blendModes = BlendMode.values; @override Widget build(BuildContext context) { return Padding( padding: const EdgeInsets.all(12.0), child: Column( children: [ Expanded( child: Container( width: double.infinity, color: Colors.grey[300], child: Image.network( 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg', fit: _fit, alignment: _alignment, color: _useColor ? Colors.red.withValues(alpha: 0.5) : null, colorBlendMode: _blendMode, ), ), ), SizedBox(height: 20), Row( children: [ Text("BoxFit:"), SizedBox(width: 8), DropdownButton<BoxFit>( value: _fit, onChanged: (value) => setState(() => _fit = value!), items: fitOptions .map( (fit) => DropdownMenuItem( value: fit, child: Text(fit.toString().split('.').last), ), ) .toList(), ), SizedBox(width: 12), Expanded(child: Text(fitDescriptions[_fit] ?? "")), ], ), Row( children: [ Text("Alignment:"), SizedBox(width: 8), DropdownButton<Alignment>( value: _alignment, onChanged: (value) => setState(() => _alignment = value!), items: alignmentOptions .map( (align) => DropdownMenuItem( value: align, child: Text(align.toString().split('.').last), ), ) .toList(), ), SizedBox(width: 12), Expanded(child: Text(alignmentDescriptions[_alignment] ?? "")), ], ), Row( children: [ Checkbox( value: _useColor, onChanged: (value) => setState(() => _useColor = value!), ), Text("Usa Color Blend"), SizedBox(width: 8), DropdownButton<BlendMode>( value: _blendMode, onChanged: (value) => setState(() => _blendMode = value!), items: blendModes .map( (mode) => DropdownMenuItem( value: mode, child: Text(mode.toString().split('.').last), ), ) .toList(), ), SizedBox(width: 12), Expanded(child: Text(blendModeDescriptions[_blendMode] ?? "")), ], ), ], ), ); } }

Icon

Descrizione

Il widget Icon serve per visualizzare icone vettoriali in un’app Flutter.
Le icone sono scalabili, personalizzabili nel colore e nella dimensione, e spesso usate per migliorare l’usabilità dell’interfaccia.

Struttura di base

Icon( Icons.star, )

Attributi utili

AttributoTipoDescrizione
iconIconDataL’icona da mostrare (es. Icons.home, Icons.settings)
sizedouble?Dimensione dell’icona in pixel logici
colorColor?Colore dell’icona
semanticLabelString?Etichetta testuale per accessibilità
textDirectionTextDirection?Direzione per invertire icone bidirezionali

Esempio pratico

Row( children: [ Icon(Icons.location_on, color: Colors.red, size: 30), SizedBox(width: 8), Text("Palermo, Sicilia"), ], )

Esercizio utile

“Crea una Row con 3 Icon diverse e assegna a ciascuna un colore e una dimensione diversa. Poi avvolgile in un Column e aggiungi un Text descrittivo sotto ognuna.”

Info utili

Una libreria di icone è un insieme di icone già pronte disponibili come costanti IconData. Flutter include già una libreria predefinita: Icons, che fa parte del pacchetto material.

ListTile

Descrizione

ListTile è un widget utile per mostrare un singolo elemento all’interno di una lista.
Fornisce una struttura predefinita con titolo, sottotitolo, icona iniziale/finale, rendendo facile creare elenchi ordinati e coerenti.

Note

È il mattoncino base per costruire liste (ListView) in Flutter.

È un ottimo esempio di come un widget combinato semplifica e velocizza il lavoro


Attributi utili

AttributoTipoDescrizione
titleWidgetIl contenuto principale (di solito Text)
subtitleWidget?Testo secondario sotto il title, opzionale
leadingWidget?Widget da mostrare all’inizio (es. Icon, CircleAvatar)
trailingWidget?Widget alla fine (es. Icon, Switch, Text)
onTapFunction()?Azione da eseguire al tap
isThreeLineboolSe true, il ListTile occupa tre righe (serve subtitle)
densebool?Usa una versione più compatta (true) o più spaziosa (false)
selectedboolEvidenzia l’elemento come selezionato
enabledboolSe false, disattiva l’interazione e mostra l’elemento in grigio

Esempio pratico

ListTile( leading: Icon(Icons.account_circle), title: Text('Mario Rossi'), subtitle: Text('Online'), trailing: Icon(Icons.chat), onTap: () { print('Chat con Mario'); }, )

Esercizio utile

Crea una ListView con 5 ListTile, ognuno con un title, subtitle, leading (un’icona diversa) e trailing (Icon(Icons.arrow_forward)). Aggiungi una onTap che stampa il nome del contatto.

Esercizio Bonus

Crea un Widget Custom che assume lo stesso comportamento del ListTile

Wrap

Descrizione

Wrap è un widget utile per permettere ai nostri wiget di andare a capo se non rientrano nello spazio dato dal parent È utile per layout flessibili e dinamici, come tag, pulsanti, icone.

Attributi utili

AttributoTipoDescrizione
directionAxisDirezione principale (horizontal o vertical)
alignmentWrapAlignmentAllineamento lungo l’asse principale
runAlignmentWrapAlignmentAllineamento delle righe/colonne secondarie
spacingdoubleSpazio orizzontale tra gli elementi
runSpacingdoubleSpazio verticale tra le righe
childrenList<Widget>Gli elementi da posizionare nel layout flessibile
  • spacing → distanza orizzontale tra gli elementi nella stessa riga

  • runSpacing → distanza verticale tra le righe (quando vanno a capo)


Esempio pratico

Wrap( spacing: 8, runSpacing: 4, children: List.generate(6, (index) { return Chip(label: Text('Item $index')); }), )

Esercizio utile

“Crea un layout con almeno 10 Chip usando Wrap. Imposta spacing: 12 e runSpacing: 8. Cambia alignment in center e osserva cosa succede al ridimensionare la finestra.”