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
| Attributo | Descrizione |
|---|---|
width, height | Imposta dimensioni fisse |
color | Colore di sfondo |
alignment | Posiziona il contenuto (Alignment.center, ecc.) |
padding | Spazio interno (usa EdgeInsets) |
margin | Spazio esterno |
decoration | Per bordi, sfondi, gradienti |
child | Il 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
| Attributo | Descrizione |
|---|---|
child | Il widget da centrare |
heightFactor, widthFactor | Moltiplicatore 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
| Attributo | Tipo | Descrizione |
|---|---|---|
padding | EdgeInsets | Spazio interno da applicare (obbligatorio) |
child | Widget | Il widget contenuto nel padding |
Come si definisce EdgeInsets?
| Metodo | Significato |
|---|---|
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
- Avvolgi 3
Textcon padding diversi (uno solo a sinistra, uno simmetrico, uno tutto attorno) e osserva come cambia il layout. - 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
| Attributo | Tipo | Descrizione |
|---|---|---|
width | double? | Larghezza del box |
height | double? | Altezza del box |
child | Widget? | Il widget da visualizzare nelle dimensioni forzate |
Uso come spazio vuoto
SizedBox(height: 20)Molto usato in
ColumnoRowper aggiungere spazio tra widget.
Esempio pratico
Column(
children: [
Text("Titolo"),
SizedBox(height: 16),
Text("Descrizione"),
],
)Esercizio utile
“Inserisci 3
Textin una colonna. UsaSizedBox(height: 32)per distanziare i primi due, eSizedBox(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
| Attributo | Tipo | Descrizione |
|---|---|---|
flex | int | Valore di flessibilità: maggiore = occupa più spazio |
Differenza con SizedBox
Spacer()occupa tutto lo spazio disponibile dinamicamenteSizedBox(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 dueSpacer()per distribuire le icone in modo uniforme. Prova a cambiare i valori diflex.”
ConstrainedBox
Limita un widget a delle dimensioni minime e/o massime.
🔹 Attributi utili
| Attributo | Descrizione |
|---|---|
constraints | Specifica i limiti (usa BoxConstraints) |
child | Il 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
Centerper centrare - Usa
ConstrainedBoxper limitare la larghezza e l’altezza a max 300px - Usa
Containercon: paddingdecorationconBoxDecorationTextcentrato all’interno- Colori e stile a tua scelta
🕓 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
| Attributo | Descrizione | Valori disponibili |
|---|---|---|
children | Lista di widget | Qualsiasi lista di widget |
mainAxisAlignment | Allinea i figli lungo l’asse verticale | start, center, end, spaceBetween, spaceAround, spaceEvenly |
crossAxisAlignment | Allinea lungo l’asse orizzontale | start, center, end, stretch, baseline |
mainAxisSize | Spazio occupato dal Column | MainAxisSize.max (default), MainAxisSize.min |
textDirection | Direzione del testo (raro) | TextDirection.ltr, TextDirection.rtl |
verticalDirection | Direzione di crescita verticale | VerticalDirection.down (default), VerticalDirection.up |
🔹 Row
Un Row organizza i widget in orizzontale, affiancati.
Row(
children: [
Icon(Icons.star),
Text("Preferito"),
],
)Attributi utili
| Attributo | Descrizione | Valori disponibili |
|---|---|---|
children | Lista di widget | Qualsiasi lista di widget |
mainAxisAlignment | Spazio tra i figli | start, center, end, spaceBetween, spaceAround, spaceEvenly |
crossAxisAlignment | Allinea verticalmente | start, center, end, stretch, baseline |
mainAxisSize | Spazio occupato dalla Row | MainAxisSize.max, MainAxisSize.min |
textDirection | Ordine dei widget | TextDirection.ltr, TextDirection.rtl |
verticalDirection | Direzione 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
| Attributo | Descrizione | Valori disponibili |
|---|---|---|
alignment | Allineamento predefinito dei figli | Alignment.center, topLeft, bottomRight, ecc. |
fit | Come i figli si adattano allo Stack | StackFit.loose (default), StackFit.expand, StackFit.passthrough |
clipBehavior | Come viene ritagliato l’overflow dei figli | Clip.none, Clip.hardEdge, Clip.antiAlias, ecc. |
children | Lista di widget sovrapposti | Qualsiasi lista di widget |
🔹 Widget speciale: Positioned
Serve per posizionare un figlio dentro uno Stack.
Positioned(
top: 10,
left: 20,
child: Text("Posizionato"),
)Attributi utili
| Attributo | Descrizione | Valori |
|---|---|---|
top | Distanza dal bordo superiore | double |
left | Distanza dal bordo sinistro | double |
right | Distanza dal bordo destro | double |
bottom | Distanza dal bordo inferiore | double |
child | Widget da posizionare | Widget |
🔹 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 |
|---|---|
crossAxisCount | Numero di colonne (solo per .count e SliverGridDelegateWithFixedCrossAxisCount) |
mainAxisSpacing | Spazio verticale tra le righe |
crossAxisSpacing | Spazio orizzontale tra le colonne |
padding | Padding attorno alla griglia |
shrinkWrap | Se true, la griglia si adatta al contenuto anziché espandersi |
physics | Comportamento dello scroll (es. NeverScrollableScrollPhysics) |
Varianti principali di GridView
| Costruttore | Descrizione |
|---|---|
GridView.count | ✅ Semplice: usa un numero fisso di colonne (crossAxisCount) |
GridView.builder | ✅ Performante: costruisce gli item su richiesta, utile per lunghe liste |
GridView.extent | Calcola il numero di colonne in base a una larghezza massima per cella per layout responsive |
GridView.custom | Per 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.custome anche internamente daGridView.count.
Le varianti di SliverGridDelegate
| Classe | Descrizione |
|---|---|
SliverGridDelegateWithFixedCrossAxisCount | ✅ Imposta un numero fisso di colonne (crossAxisCount) |
SliverGridDelegateWithMaxCrossAxisExtent | ✅ Imposta una larghezza massima per le celle, e calcola quante mostrarne |
SliverChildDelegate | Usato 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')),
);
},
)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
| Attributo | Descrizione |
|---|---|
flex | Peso del widget rispetto agli altri |
child | Il 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:
ColumneRowper disporre elementiStackper sovrapporre contenutiContainerper stile, colore e dimensioni
Cosa deve contenere
- Una
Columncon 3Containercolorati - Una
Rowcon 2 box affiancati - Uno
Stackcon unContainerdi sfondo e unTextsovrapposto
SingleChildScrollView
Descrizione
SingleChildScrollView è un widget che permette di scorrere un solo figlio verticalmente o orizzontalmente.
Molto utile quando si ha contenuto lungo all’interno di una colonna.
Attributi utili
| Attributo | Tipo | Descrizione |
|---|---|---|
child | Widget | Il widget da visualizzare all’interno dello scroll |
scrollDirection | Axis | Direzione di scorrimento: Axis.vertical o Axis.horizontal |
padding | EdgeInsets | Spaziatura interna |
reverse | bool | Inverte 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
SingleChildScrollViewverticale.
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 |
|---|---|
style | Permette di applicare uno stile tramite TextStyle (colore, fontSize, fontWeight, ecc.) |
textAlign | Definisce l’allineamento del testo (es: TextAlign.center, TextAlign.right, ecc.) |
maxLines | Imposta il numero massimo di righe (utile per troncare il testo) |
overflow | Definisce il comportamento in caso di testo troppo lungo (ellipsis, fade, ecc.) |
softWrap | Se false, disabilita l’andata a capo automatica |
textDirection | Direzione del testo: TextDirection.ltr o rtl |
locale | Imposta una localizzazione specifica per la formattazione |
semanticsLabel | Label 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)
| Variante | Descrizione |
|---|---|
TextOverflow.clip | ✂️ Taglia il testo alla fine della riga, senza aggiungere nulla. Il contenuto oltre il limite è semplicemente rimosso. |
TextOverflow.ellipsis | … Aggiunge 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),
),
],
),
)Usa i Theme.of(context).textTheme per mantenere uno stile coerente con il tema dell’app!
Esercizio utile
Crea una schermata con 3
Textdifferenti:
- Uno con
fontSize: 30,fontWeight: FontWeight.bold.- Uno centrato in corsivo con colore grigio.
- Uno lungo, tagliato con
overflow: TextOverflow.ellipsisemaxLines: 1.
Esercizio Bonus
Crea un widget
CustomTitleche:
- prende un
Stringcome input,- usa
Textper visualizzarlo,- ha colore personalizzabile,
- calcola automaticamente
fontSizein 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
fontFamilydeve 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.ttf3. Usa il font nel codice
Text(
'Titolo con font personalizzato',
style: TextStyle(fontFamily: 'Montserrat'),
)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
| Attributo | Descrizione |
|---|---|
height | Settare un altezza |
width | Settare una larghezza |
fit | Determina come l’immagine si adatta allo spazio disponibile all’interno del contenitore |
alignment | Definisce come viene posizionata l’immagine all’interno del contenitore, soprattutto se l’immagine è più piccola dello spazio disponibile (es. in BoxFit.none o scaleDown) |
color | Se non è null, il colore fornito viene sovrapposto all’immagine con l’effetto specificato in colorBlendMode |
colorBlendMode | Specifica 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ì:
- Creare una directory
assets/imagesnella directory principale - Inserire la voce
assets:su pubspec sotto la voceflutter: - 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.pngper 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
| Valore | Ritaglia? | Antialias? | Performance | Quando 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ù lento | Quando 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. conClip.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
| Attributo | Tipo | Descrizione |
|---|---|---|
icon | IconData | L’icona da mostrare (es. Icons.home, Icons.settings) |
size | double? | Dimensione dell’icona in pixel logici |
color | Color? | Colore dell’icona |
semanticLabel | String? | Etichetta testuale per accessibilità |
textDirection | TextDirection? | 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
Rowcon 3Icondiverse e assegna a ciascuna un colore e una dimensione diversa. Poi avvolgile in unColumne aggiungi unTextdescrittivo 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.
È il mattoncino base per costruire liste (ListView) in Flutter.
È un ottimo esempio di come un widget combinato semplifica e velocizza il lavoro
Attributi utili
| Attributo | Tipo | Descrizione |
|---|---|---|
title | Widget | Il contenuto principale (di solito Text) |
subtitle | Widget? | Testo secondario sotto il title, opzionale |
leading | Widget? | Widget da mostrare all’inizio (es. Icon, CircleAvatar) |
trailing | Widget? | Widget alla fine (es. Icon, Switch, Text) |
onTap | Function()? | Azione da eseguire al tap |
isThreeLine | bool | Se true, il ListTile occupa tre righe (serve subtitle) |
dense | bool? | Usa una versione più compatta (true) o più spaziosa (false) |
selected | bool | Evidenzia l’elemento come selezionato |
enabled | bool | Se 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
ListViewcon 5ListTile, ognuno con untitle,subtitle,leading(un’icona diversa) etrailing(Icon(Icons.arrow_forward)). Aggiungi unaonTapche 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
| Attributo | Tipo | Descrizione |
|---|---|---|
direction | Axis | Direzione principale (horizontal o vertical) |
alignment | WrapAlignment | Allineamento lungo l’asse principale |
runAlignment | WrapAlignment | Allineamento delle righe/colonne secondarie |
spacing | double | Spazio orizzontale tra gli elementi |
runSpacing | double | Spazio verticale tra le righe |
children | List<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
ChipusandoWrap. Impostaspacing: 12erunSpacing: 8. Cambiaalignmentincentere osserva cosa succede al ridimensionare la finestra.”