Compreso il funzionamento di Flutter e dei suoi componenti base, iniziamo a compiere i primi passi per la creazione di layout più articolati per suddividere lo spazio della schermata in righe o colonne e per sovrapporre tra loro due widget.
In questa lezione vedremo nel dettaglio i widget Row
e Column
offerti da Flutter per la creazione di Multi-Child layout.
Row e Column Widget
Uno dei principali task nello sviluppo di un’app è quello di collocare correttamente gli elementi all’interno della schermata. Questo processo può avvenire in diversi modi, come ad esempio organizzare la schermata in righe e/o in colonne per disporre in modo appropriato gli elementi che contiene.
Per fare ciò, Flutter dispone dei widget Row
e Cloumn
, che risultano tra loro complementari e permettono l’organizzazione dei widget figli rispettivamente in righe e colonne. Nessuno dei due widget permette lo scroll orizzontale o verticale e qualora fosse necessario visualizzare un numero elevato di elementi è consigliabile usare una ListView
.
Nel caso in cui non fosse noto a priori se usare una Row
o una Column
per la visualizzazione dei dati, Flutter mette a disposizione il widget Flex
, che permette di controllare su quale asse (orizzontale o verticale) andare a disporre i figli. È consigliabile però sapere sempre come visualizzare i dati, in quanto l'uso di Flex
può risultare eccessivamente verboso.
Infine, nel caso ci sia un solo elemento da visualizzare, invece di usare Flex
, Row
o Column
è consigliabile utilizzare i widget Align
o Center
per posizionarlo nella schermata.
Le proprietà
A differenza degli altri widget definiti finora, composti da proprietà eterogenee, i widget Row
e Column
definiscono il medesimo set di proprietà, in quanto il primo è semplicemente una variante del secondo e viceversa. Tra le principali proprietà che è fondamentale conoscere ritroviamo:
Proprietà | Tipo Accettato | Descrizione |
---|---|---|
children |
List<Widget> |
è la lista di widget contenuti all’interno di questo componente e sottoalbero |
crossAxisAlignment |
CrossAxisAlignment |
rappresenta come devono essere i widget figli posizionati lungo gli assi attraverso l’utilizzo delle proprietà offerte dal widget CrossAxisAlignment |
direction |
Axis |
la direzione da utilizzare per l’asse principale. Per Row e Column questa proprietà è già fissata all’asse appropriato, ossia orizzontale per Row e verticale per Column |
mainAxisAlignment |
MainAxisAlignment |
definisce come posizionare i widget figli attorno all’asse principale |
mainAxisSize |
MainAxisSize |
rappresenta quanto spazio deve essere occupato nell’asse principale |
textDirection |
TextDirection |
determina la disposizione dei widget figli orizzontalmente e come interpretare lo start e end nella direzione orizzontale |
verticalDirection |
VerticalDirection |
fornisce la disposizione dei widget figli verticalmente e definisce come interpretare lo start e end nella direzione verticale |
Comprese le proprietà di questi due widget simili tra loro, vediamo qualche esempio pratico.
Esempi pratici
Per iniziare, creiamo un nuovo progetto come mostrato nella lezione 6 di questa guida e, lasciando inalterati gli import, il metodo main()
e la classe MyApp
, cancelliamo il resto per aggiungere il seguente StatelessWidget
composto da uno Scaffold
per la definizione dell’AppBar
e da una SingleChildScrollView
(di cui abbiamo già parlato nella lezione 11), che conterrà tutti i nostri esempi pratici di import delle immagini.
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Lesson 16'),
),
body: SingleChildScrollView(
child: Column(
children: <Widget>[
//we will add our widgets here.
],
)),
);
}
}
Modifichiamo infine la classe MyApp
per impostare il widget appena creato come home.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// . . .
home: MyPage(),
);
}
}
Ora siamo pronti per le nostre sperimentazioni.
Partiamo subito da un semplice utilizzo di questi due widget.
Container(
color: Colors.orange[100],
child: Row(children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
)
])),
// . . .
Container(
color: Colors.orange[100],
child: Column(children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
)
])),
In questo caso, sia per il widget Row
che Column
abbiamo definito due Text
che si dispongono in ordine lungo gli assi orizzontali e verticali, rispettivamente. In particolare, per rendere evidente l’effetto riga e colonna, abbiamo inserito i suddetti widget in appositi container di cui è stato definito il colore.
Eseguendo l’app otteniamo il risultato in figura.
Passiamo adesso all’esplorazione della proprietà mainAxisAlignment
.
Definiamo all’interno del nostro StatelessWidget
un nuovo metodo che tornerà un oggetto di tipo Widget
e che prende in ingresso il valore desiderato per mainAxisAlignment
.
Widget _rowMainAlign(mainAxisAlignment) => Container(
color: Colors.lightBlue[50],
child: Row(
mainAxisAlignment: mainAxisAlignment,
children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text3",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text4",
style: TextStyle(color: Colors.black, fontSize: 17),
),
],
),
height: 30,
margin: EdgeInsets.all(5),
);
In particolare, in questo metodo abbiamo definito un Container
composto da un background colorato e avente come figlio un widget Row
che presenta:
- la proprietà
mainAxisAlignment
impostata al valore passato per parametro; - un lista di widget
Text
.
In questo modo, richiamando il metodo con le diverse proprietà fornite dal widget MainAxisAlignment
potremo vedere il diverso comportamento di questa proprietà.
Ad esempio, impostando passando come parametro le proprietà MainAxisAlignment.start
e MainAxisAlignment.center
otteremo quanto segue.
In questo caso, gli elementi verranno spostati all’inizio o al centro della riga. Utilizzando le altre proprietà offerte da MainAxisAlignment
otterremo il seguente risultato.
In particolare, è interessante porre l’attenzione sulle proprietà spaceBetween
, spaceEvenly
, e spaceAround
che aggiungono uno spazio tra i widget figli della Row
in modo differente, come si è potuto notare.
Prorpietà | Descrizione |
---|---|
spaceBetween |
aggiunge uno spazio tra i widget figli spingendo il primo e l’ultimo elemento agli estremi |
spaceEvenly |
aggiunge uno spazio prima e dopo i widget figli affinché esso sia uguale per tutti |
spaceAround |
aggiunge uno spazio attorno ai widget che si somma nel caso di due widget vicini tra loro, aumentando la distanza |
Similmente, possiamo ripetere lo stesso esperimento con il widget Column
, scrivendo un metodo analogo (disponibile su GitHub).
Pertanto utilizzando le proprietà start
, center
ed end
otterremo il seguente risultato.
Vediamo ora la proprietà crossAxisAlignment
.
Come si è potuto notare, la proprietà mainAxisAlignment
lavora orizzontalmente per le Row
e verticalmente per le Column
. La proprietà crossAxisAlignment
è invece perpendicolare alla prima e quindi agirà verticalmente sulle Row
e orizzontalmente sulle Column
.
Vediamo un esempio di utilizzo tramite il widget Row
. Definiamo un metodo che restituisca un nuovo widget Row
composto da quattro widget Text
e avente la proprietà crossAxisAlignment
impostata al valore passato da parametro.
Widget _rowCrossAxisAlignment(crossAxisAlignment) => Container(
color: Colors.lightGreen[50],
child: Row(
crossAxisAlignment: crossAxisAlignment,
children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text3",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text4",
style: TextStyle(color: Colors.black, fontSize: 17),
),
],
),
height: 50,
margin: EdgeInsets.all(5),
);
Invochiamo quindi il metodo appena scritto passando come parametro le proprietà di CrossAxisAlignment
quali start
, center
ed end
per ottenere il seguente risultato.
Come si può notare i quattro widget Text
sono stati disposti in alto, al centro e in basso all’interno del container.
Analogamente, possiamo ripetere lo stesso esperimento con il widget Column
scrivendo un metodo analogo e disponibile al seguente link.
Pertanto utilizzando le proprietà start
, center
ed end
otterremo il seguente risultato.
Esaminiamo, adesso, la proprietà mainAxisAlignment
.
Essa permette di definire la dimensione dell’asse principale di entrambi i widget. In particolare, possiamo assegnare a questa proprietà due valori:
Valore | Descrizione |
---|---|
MainAxisAlignment.min |
assegna la minima dimensione possibile al widget, che sarà grande quanto la somma totale delle dimensioni dei suoi widget figli |
MainAxisAlignment.max |
assegna la massima dimensione possibile al widget |
Vediamo un semplice esempio con il widget Row
, in quanto per il widget Column
il comportamento sarà il medesimo ma sull’asse verticale.
Come già fatto in precedenza, definiamo un nuovo metodo che ritorna un Widget
e prende come parametro di ingresso un valore per mainAxisSize
.
Widget _rowMainAxisSize(mainAxisSize) => Container(
color: mainAxisSize == MainAxisSize.min ? Colors.pink : Colors.yellowAccent,
child: Row(
mainAxisSize: mainAxisSize,
children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text3",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text4",
style: TextStyle(color: Colors.black, fontSize: 17),
),
],
));
In particolare, questo metodo definisce quattro widget Text
come figli della Row
e imposta il mainAxisSize
al valore passato per parametro.
Invocando perciò il metodo con i valori di min
e max
, otterremo quanto segue.
Chiudiamo infine questa sezione di esempi con l’utilizzo della proprietà verticalDirection
.
Il comportamento cambia in base a quale widget usiamo e a quali proprietà sono definite. Per ulteriori dettagli rimandiamo alla documentazione ufficiale. In questa sede, ci limiteremo a mostrare un semplice esempio di utilizzo con Column
.
Definiamo un nuovo metodo che ha come scopo quello di creare un widget Column
e di impostare la verticalDirection
passata da parametro.
Widget _columnVerticalDirection(verticalDirection) => Container(
color: Colors.lightBlue[50],
child: Column(
verticalDirection: verticalDirection,
children: <Widget>[
Text(
"Text1",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text2",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text3",
style: TextStyle(color: Colors.black, fontSize: 17),
),
Text(
"Text4",
style: TextStyle(color: Colors.black, fontSize: 17),
),
],
),
height: 150,
margin: EdgeInsets.all(5),
);
Invochiamo ora il metodo passando come parametro il valore down
e up
di VerticalDirection
ottenendo il risultato in figura.
In questo caso, con la proprietà down
gli elementi vengono disposti dall’alto verso il basso, mentre con la proprietà up
sono disposti dal basso verso l’alto.
Il codice di questa lezione è disponibile su GitHub.