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
Text
con 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
Column
oRow
per aggiungere spazio tra widget.
Esempio pratico
Column(
children: [
Text("Titolo"),
SizedBox(height: 16),
Text("Descrizione"),
],
)
Esercizio utile
“Inserisci 3
Text
in 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
Center
per centrare - Usa
ConstrainedBox
per limitare la larghezza e l’altezza a max 300px - Usa
Container
con: padding
decoration
conBoxDecoration
Text
centrato 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.custom
e 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:
Column
eRow
per disporre elementiStack
per sovrapporre contenutiContainer
per stile, colore e dimensioni
Cosa deve contenere
- Una
Column
con 3Container
colorati - Una
Row
con 2 box affiancati - Uno
Stack
con unContainer
di sfondo e unText
sovrapposto
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
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 |
---|---|
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
Text
differenti:
- Uno con
fontSize: 30
,fontWeight: FontWeight.bold
.- Uno centrato in corsivo con colore grigio.
- Uno lungo, tagliato con
overflow: TextOverflow.ellipsis
emaxLines: 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'),
)
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/images
nella 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.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
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
Row
con 3Icon
diverse e assegna a ciascuna un colore e una dimensione diversa. Poi avvolgile in unColumn
e aggiungi unText
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.
È 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
ListView
con 5ListTile
, ognuno con untitle
,subtitle
,leading
(un’icona diversa) etrailing
(Icon(Icons.arrow_forward)
). Aggiungi unaonTap
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
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
Chip
usandoWrap
. Impostaspacing: 12
erunSpacing: 8
. Cambiaalignment
incenter
e osserva cosa succede al ridimensionare la finestra.”