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.

Attributi utili

ProprietàDescrizione
crossAxisCountNumero di colonne (solo per .count e SliverGridDelegateWithFixedCrossAxisCount)
mainAxisSpacingSpazio verticale tra le righe
crossAxisSpacingSpazio orizzontale tra le colonne
paddingPadding attorno alla griglia
shrinkWrapSe true, la griglia si adatta al contenuto anziché espandersi
physicsComportamento dello scroll (es. NeverScrollableScrollPhysics)

Varianti principali di GridView

CostruttoreDescrizione
GridView.count✅ Semplice: usa un numero fisso di colonne (crossAxisCount)
GridView.builder✅ Performante: costruisce gli item su richiesta, utile per lunghe liste
GridView.extentCalcola il numero di colonne in base a una larghezza massima per cella per layout responsive
GridView.customPer layout completamente personalizzati con delegati

gridDelegate: cos’è e perché è importante

Il parametro gridDelegate controlla come viene distribuito lo spazio all’interno della griglia: dimensioni delle celle, spaziatura, numero di colonne o larghezza massima, ecc.

Viene usato principalmente con GridView.builder, GridView.custom e anche internamente da GridView.count.

Le varianti di SliverGridDelegate

ClasseDescrizione
SliverGridDelegateWithFixedCrossAxisCount✅ Imposta un numero fisso di colonne (crossAxisCount)
SliverGridDelegateWithMaxCrossAxisExtent✅ Imposta una larghezza massima per le celle, e calcola quante mostrarne
SliverChildDelegateUsato per gestire i figli in GridView.custom, offre il massimo controllo

GridView.count()
GridView.count( crossAxisCount: 2, children: [ Container(color: Colors.blue), Container(color: Colors.green), ], )
GridView.builder()
GridView.builder( itemCount: 20, gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 8, mainAxisSpacing: 8, ), itemBuilder: (context, index) { return Container( color: Colors.primaries[index % Colors.primaries.length], child: Center(child: Text('Item $index')), ); }, )
💡
Tip

GridView.builder è ideale per griglie generate da dati, come una lista di prodotti, immagini o post.


🔹 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), ), ], )

Esercitazione 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

SingleChildScrollView

Descrizione

SingleChildScrollView è un widget che permette di scorrere un solo figlio verticalmente o orizzontalmente.

Note

Molto utile quando si ha contenuto lungo all’interno di una colonna.


Attributi utili

AttributoTipoDescrizione
childWidgetIl widget da visualizzare all’interno dello scroll
scrollDirectionAxisDirezione di scorrimento: Axis.vertical o Axis.horizontal
paddingEdgeInsetsSpaziatura interna
reverseboolInverte l’ordine dello scroll (utile per chat)

Esempio pratico

SingleChildScrollView( padding: EdgeInsets.all(16), child: Column( children: List.generate(20, (index) => Text('Elemento $index')), ), )

Esercizio utile

Crea una pagina con un numero di widget minimo da non rientrare sullo schermo, racchiuse in uno SingleChildScrollView verticale.


Widget per il contenuto

Text

Il widget Text in Flutter serve per mostrare stringhe di testo statico all’interno dell’interfaccia utente. È uno dei widget più usati, ed è estremamente personalizzabile tramite lo stile, l’allineamento e il comportamento del testo.


Esempio base

Text('Hello, World!')

Questo mostrerà una semplice riga di testo con lo stile predefinito del tema.


Proprietà utili del widget Text

ProprietàDescrizione
stylePermette di applicare uno stile tramite TextStyle (colore, fontSize, fontWeight, ecc.)
textAlignDefinisce l’allineamento del testo (es: TextAlign.center, TextAlign.right, ecc.)
maxLinesImposta il numero massimo di righe (utile per troncare il testo)
overflowDefinisce il comportamento in caso di testo troppo lungo (ellipsis, fade, ecc.)
softWrapSe false, disabilita l’andata a capo automatica
textDirectionDirezione del testo: TextDirection.ltr o rtl
localeImposta una localizzazione specifica per la formattazione
semanticsLabelLabel alternativa per screen reader/accessibilità

Personalizzazione con style

Per personalizzare il testo, puoi usare la proprietà style, che accetta un oggetto TextStyle.

Text( 'Titolo', style: TextStyle( fontSize: 24, fontWeight: FontWeight.bold, color: Colors.blue, letterSpacing: 1.2, ), )

Troncamento del testo (overflow e maxLines)

VarianteDescrizione
TextOverflow.clip✂️ Taglia il testo alla fine della riga, senza aggiungere nulla. Il contenuto oltre il limite è semplicemente rimosso.
TextOverflow.ellipsisAggiunge tre puntini () alla fine della riga, indicando che c’è più testo non visibile. È la scelta più comune.
TextOverflow.fade🎞️ Sfuma il testo alla fine, rendendolo trasparente gradualmente. Utile per effetti grafici eleganti.
TextOverflow.visible🚫 Mostra tutto il testo, anche se va oltre i limiti del layout. Può causare overflow visivo (errore rosso a schermo).
Text( 'Questo è un testo molto lungo che verrà troncato...', maxLines: 1, overflow: TextOverflow.ellipsis, )

Uso avanzato: rich text (testo con stili misti)

Per avere più stili all’interno dello stesso testo, usa RichText con TextSpan:

RichText( text: TextSpan( text: 'Flutter ', style: TextStyle(color: Colors.black, fontSize: 18), children: [ TextSpan( text: 'è fantastico', style: TextStyle(fontWeight: FontWeight.bold, color: Colors.blue), ), ], ), )
💡
Tip

Usa i Theme.of(context).textTheme per mantenere uno stile coerente con il tema dell’app!


Esercizio utile

Crea una schermata con 3 Text differenti:

  • Uno con fontSize: 30, fontWeight: FontWeight.bold.
  • Uno centrato in corsivo con colore grigio.
  • Uno lungo, tagliato con overflow: TextOverflow.ellipsis e maxLines: 1.

Esercizio Bonus

Crea un widget CustomTitle che:

  • prende un String come input,
  • usa Text per visualizzarlo,
  • ha colore personalizzabile,
  • calcola automaticamente fontSize in base alla lunghezza della stringa,
  • mostra massimo 2 righe con TextOverflow.ellipsis.

Cambiare il font

Flutter ti permette di cambiare il font localmente su un solo widget oppure globalmente in tutta l’app. Puoi usare font di sistema oppure caricare font personalizzati.


Cambiare font localmente con TextStyle

Text( 'Ciao!', style: TextStyle( fontFamily: 'Roboto', fontSize: 20, ), )

Il valore di fontFamily deve corrispondere al nome del font disponibile nel sistema o dichiarato negli asset.


Cambiare font globalmente nel ThemeData

MaterialApp( theme: ThemeData( fontFamily: 'Montserrat', // Tutti i Text() useranno questo font ), home: MyHomePage(), )

Usare un font personalizzato

1. Aggiungi il font al progetto

Crea una cartella nel tuo progetto, ad esempio: assets/fonts/

Inserisci il file .ttf (es. Montserrat-Regular.ttf).


2. Registra il font nel pubspec.yaml
flutter: fonts: - family: Montserrat fonts: - asset: assets/fonts/Montserrat-Regular.ttf

3. Usa il font nel codice
Text( 'Titolo con font personalizzato', style: TextStyle(fontFamily: 'Montserrat'), )
💡
Tip

Il font deve essere caricato correttamente o Flutter tornerà a quello di default.


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, ... )

ClipRRect, ClipOval, ClipPath, e Container con clipBehavior

Quando usiamo immagini (o qualsiasi widget), può essere utile ritagliarle per ottenere effetti visivi come angoli arrotondati, cerchi, o forme personalizzate. Flutter offre diversi widget per gestire il clipping.

//Usato per ritagliare un widget (es. immagine) all’interno di un rettangolo con angoli arrotondati. ClipRRect( borderRadius: BorderRadius.circular(20), child: Image.network('https://picsum.photos/200'), ) //Usato per ritagliare il contenuto in una forma ovale o circolare (funziona bene su contenitori quadrati). ClipOval( child: Image.network( 'https://picsum.photos/200', width: 100, height: 100, fit: BoxFit.cover, ), ) //Consente di ritagliare un widget secondo una forma completamente personalizzata, definita da un oggetto CustomClipper<Path>. ClipPath( clipper: TriangleClipper(), child: Image.network('https://picsum.photos/300'), ) class TriangleClipper extends CustomClipper<Path> { @override Path getClip(Size size) { final path = Path(); path.moveTo(size.width / 2, 0); // top path.lineTo(0, size.height); // bottom left path.lineTo(size.width, size.height); // bottom right path.close(); return path; } @override bool shouldReclip(CustomClipper<Path> oldClipper) => false; } // Container di per sé non ritaglia il contenuto. Ma se aggiungiamo: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(20), ), clipBehavior: Clip.antiAlias, child: Image.network('https://picsum.photos/200'), )

clipBehavior: tutti i valori disponibili

ValoreRitaglia?Antialias?PerformanceQuando usarlo
Clip.none❌ No❌ No✅✅✅Quando vuoi lasciare sbordare il contenuto senza limiti visivi
Clip.hardEdge✅ Sì❌ No✅✅Ritaglio netto senza sfumature: layout rettangolari e spigolosi
Clip.antiAlias✅ Sì✅ SìPer contenuti con angoli arrotondati o forme ovali, bordi lisci
Clip.antiAliasWithSaveLayer✅ Sì✅ Sì❌ più lentoQuando servono trasparenze, ombre interne o blend complessi, come nei layer

[!tip] Il valore predefinito in molti widget è Clip.none. Per ottenere un effetto visivo controllato, spesso va modificato manualmente (es. con Clip.antiAlias).

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.”