Nella lezione precedente, abbiamo preso familiarità con il framework e compreso le differenze che vi sono tra gli stateless e gli stateful widget e i rispettivi metodi per disegnare il widget ed il suo contenuto.
Flutter mette a disposizione dello sviluppatore diverse tipologie di widget pronte all’utilizzo che ci permetteranno di costruire a nostra volta nuovi widget per comporre l’interfaccia utente della nostra applicazione.
In questa lezione, analizzeremo e faremo alcuni esempi di utilizzo di uno dei principali Widget
offerti da Flutter, il widget Container
.
Container
Il widget Container
è tra i più utilizzati durante la realizzazione dell’interfaccia utente di un’app, in quanto combina tra loro diverse tipologie di widget riguardanti la posizione e le dimensioni. Come stesso il nome lascia intendere, questo widget funge da contenitore per un widget figlio gestendone le dimensioni, il background e molto altro ancora.
Le proprietà
Come tutti i widget che abbiamo visto nel corso di queste lezioni, il widget Container
fornisce un insieme di proprietà che, se definite, ne modificano il comportamento nella caratterizzazione del proprio layout. Tra queste ritroviamo:
Proprietà | Tipo Accettato | Descrizione |
---|---|---|
alignment |
AlignmentGeometry |
allinea il widget figlio in relazione al Container attraverso la definizione di un widget di tipo AlignmentGeometry come Alignment o AlignmentDirectional |
child |
Widget |
è la proprietà da utilizzare per associare all’istanza del Container da creare un widget figlio |
constraints |
BoxConstraints |
definisce ulteriori vincoli da applicare al widget figlio. Per fare ciò va definito un oggetto di tipo BoxConstraints per definire la massima e minima dimensione in altezza e larghezza |
decoration |
Decoration |
attraverso questa proprietà è possibile definire una descrizione di come deve essere disegnato il Container . Ciò è reso possibile dalla classe Decoration estesa dalle classi BoxDecoration e ShapeDecoration che analizzeremo più avanti negli esempi |
margin |
EdgeInsetsGeometry |
questa proprietà permette di definire uno spazio attorno alla decoration e al widget figlio definendo un’istanza di EdgeInsets o EdgeInsetsDirectional . Entrambe le classi estendono EdgeInsetsGeometry |
padding |
EdgeInsetsGeometry |
diversamente dalla proprietà margin , il padding permette di definire uno spazio all’interno della decoration e del widget figlio. Per definire il padding può essere istanziata una delle classi a scelta tra EdgeInsets o EdgeInsetsDirectional in base alle proprie esigenze |
transform |
Matrix4 |
questa proprietà offre la possibilità di definire una matrice di trasformazione da applicare al Container prima che venga disegnato. In particolare è possibile definire una trasformata per modificare le coordinate del Container all’interno del nodo padre. Per fare ciò viene utilizzata la classe Matrix4 che richiede la definizione di alcune operazioni matematiche per ruotare, traslare o scalare il Container |
Comportamento nella definizione del layout
Come si è potuto evincere dalla rapida panoramica delle proprietà offerte, il widget Container
combina un insieme di widget diversi, ognuno dei quali è caratterizzato da un un proprio comportamento per il layout. Ciò rende la gestione del layout del Container
più articolata. In generale, il Container
cercherà di eseguire in ordine le seguenti operazioni:
- rispettare l’allineamento imposto;
- modificare le proprie dimensioni al fine di adattarsi a quelle del proprio widget figlio;
- rispettare l’altezza, la larghezza ed i vincoli indicati;
- espandersi per adattarsi al nodo padre;
- essere il più piccolo possibile.
Entrando più nello specifico, possiamo distinguere diversi casi, riassunti nella seguente tabella.
Caso | Descrizione |
---|---|
il Container non ha nessun widget figlio e non sono definite l’altezza, la larghezza e la proprietà constraints. Inoltre il widget padre non pone vincoli |
il Container cerca di assumere le dimensioni più piccole possibili |
il Container non ha nessun widget figlio, altezza, larghezza, vincoli e alcun allineamento, ma il widget padre fornisce vincoli limitati |
il Container si espande per adattarsi ai vincoli forniti dal genitore |
il Container non ha un widget figlio e non è stata definita la proprietà alignment , ma l'altezza, la larghezza o la proprietà constraints sono definite |
il Container cerca di essere il più piccolo possibile data la combinazione di questi vincoli e dei vincoli del nodo padre |
il Container definisce a proprietà alignment e il widget padre fornisce vincoli illimitati |
il Container cerca di ridimensionarsi attorno alle dimensioni del widget figlio |
il Container ha un allineamento e il widget padre fornisce vincoli limitati |
il Container tenta di espandersi per adattarsi al widget padre e posiziona il widget figlio al suo interno in base a quanto definito dalla proprietà alignment |
il Container ha un figlio ma non sono definite le dimensioni e le proprietà constraints ed alignment |
il Container passa i vincoli imposti dal widget padre al proprio widget figlio e adatta le proprie dimensioni a quest’ultimo |
Esempi pratici
Vediamo adesso alcuni esempi pratici per creare diverse tipologie di Container
utilizzando le proprietà offerte da questo widget.
Per iniziare, creiamo un nuovo progetto o partiamo dal progetto di esempio creato nella lezione 7 e modifichiamo il file main.dart all’interno del package lib come segue.
Lasciando intatti l’import
iniziale e il metodo main
, modifichiamo la classe MyApp
.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Lesson 11 - Container Widget',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: null,
);
}
}
Come possiamo notare, abbiamo costruito un nuovo StatelessWidget
composto dalle proprietà title
, theme
, e home
. Quest’ultima verrà inizializzata con un nuovo widget, al cui interno riporteremo i nostri esempi.
Creiamo quindi la classe ContainerExample
come uno semplice StatelessWidget
che conterrà una lista di diversi esempi di Container
che possiamo costruire.
Iniziamo quindi con la creazione di questa classe e di un semplice Container
avente come figlio un widget Text
.
class ContainerExample extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Lesson 11 - Container Widget'),
),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Container(
color: Colors.orange,
child: Text("Basic Container"),
)
],
)));
}
}
Analizzando il codice scritto possiamo notare che abbiamo creato un nuovo widget Scaffold
che definisce un appBar
e un body
composto dal widget SingleChildScrollView
, estremamente utile quando abbiamo un unico elemento che potrebbe non essere interamente visibile nello schermo del dispositivo. In questo esempio, abbiamo definito come figlio un widget Column
che mostra i suoi figli in un array verticale ed ha come widget figlio una lista di Container
. In questo caso, abbiamo inserito nel nostro array di Widget
solo un Container
di cui abbiamo definito le proprietà color
e child
.
È importante comprendere che, sebbene sia una soluzione per mostrare tanti elementi in una lista, questa non è la soluzione più efficiente per mostrare tanti widget. A tal proposito si consiglia di utilizzare una ListView
che vedremo più avanti nel corso di questa guida.
Eseguiamo quindi l’applicazione, ottenendo il seguente risultato.
Aggiungiamo ora, attraverso le proprietà width
e height
, le dimensioni del Container
.
children: <Widget>[
//. . .
Container(
width: 300.0,
height: 50.0,
color: Colors.teal,
child: Text("Container width = 300 , height = 50"),
),
]
Eseguendo l’applicazione avremo il seguente risultato in cui è presente anche il precedente Container
.
Figura 58. Visualizzazione del Container con le dimensioni impostate e del precedente Container per a) Android e b) iOS (click per ingrandire)
Contrariamente, se volessimo aggiungere le proprietà margin
e padding
al Container
possiamo utilizzare la classe EdgeInsets
, creata ad esempio tramite il costruttore only()
.
children: <Widget>[
// . . .
Container(
color: Colors.yellow,
margin:
EdgeInsets.only(left: 10.0, right: 50.0, top: 10, bottom: 30),
padding:
EdgeInsets.only(left: 25.0, right: 25.0, top: 10, bottom: 10),
child: Container(
color: Colors.orangeAccent[200],
child: Text("Container with margin and padding"),
),
),
]
Effettuiamo l’Hot Reload dell’app ottenendo il seguente risultato.
Figura 59. Visualizzazione del Container con le proprietà padding e margin definite e dei precedenti Container per a) Android e b) iOS (click per ingrandire)
Come si può notare, abbiamo aggiunto dello spazio intorno al Container
giallo (ossia il container più esterno) mentre quello interno di colore arancione non si trova in alto a sinistra come nei precedenti due container, ma è più spostato per via del padding
che ha aggiunto dello spazio interno al widget.
Creiamo adesso un Container
in cui il testo al suo interno è posizionato in basso a sinistra. Per farlo, possiamo sfruttare la proprietà alignment
che accetta un oggetto di tipo AlignmentGeometry
come Alignment
. Per rendere questa modifica visibile impostiamo anche la proprietà height
del Container
come segue.
children: <Widget>[
// . . .
Container(
color: Colors.lightBlue,
alignment: Alignment.bottomRight,
height: 200,
child: Text("Container with child alignment"),
),
]
Eseguiamo quindi l’Hot Reload per visualizzare la modifica effettuata.
Figura 60. Visualizzazione del Containter con allineamento in basso a sinistra del widget figlio e dei precedenti Container per a) Android e b) iOS (click per ingrandire)
Complichiamo leggermente lo scenario e definiamo un Container che definisca le proprietà:
constraint
creando unBoxConstaint
;padding
tramite il costruttoreall()
diEdgeInsets
;decoration
utilizzando l’oggettoShapeDecoration
.
In particolare, immaginiamo di dover creare un box con i bordi arrotondati. Per farlo dovremo definire la proprietà shape di ShapeDecoration
creando l’oggetto RoundedRectangleBorder
come segue.
children: <Widget>[
// . . .
Container(
constraints: BoxConstraints.expand(height: 100.0, width: 200),
padding: EdgeInsets.all(10),
decoration: ShapeDecoration(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(20.0),
),
),
color: Colors.red),
child: Text("Container with ShapeDecoration"),
),
]
Tramite l’Hot Reload potremo vedere la modifica effettuata.
Figura 61. Visualizzazione del Container dai bordi arrotondati e dei precedenti Container per a) Android e b) iOS (click per ingrandire)
Chiudiamo infine i nostri esempi definendo una trasformata. In particolare vogliamo ruotare sull’asse delle ascisse il nostro Container
. Definiamo quindi la proprietà transform usando il metodo rotationY
di Matrix4
come segue.
children: <Widget>[
// . . .
Container(
padding: EdgeInsets.only(top: 10, left: 10),
constraints: BoxConstraints.expand(width: 200, height: 150),
color: Colors.lightGreenAccent[700],
transform: Matrix4.rotationY(pi / 5)..rotateX(pi / 5),
child: Text("this.transform"),
)
]
Eseguiamo nuovamente l’app ottenendo il seguente risultato.
Figura 62. Visualizzazione del Container ruotato e dei precedenti Container per a) Android e b) iOS (click per ingrandire)
Il codice di questa lezione è disponibile al seguente link.