Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial
  • Lezione 11 di 37
  • livello intermedio
Indice lezioni

Container Widget: i Single-Child Layout

Flutter offre diversi widget per creare l’interfaccia utente di un'app: vediamo come usare il widget Container, uno dei principali.
Flutter offre diversi widget per creare l’interfaccia utente di un'app: vediamo come usare il widget Container, uno dei principali.
Link copiato negli appunti

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.

Figura 57. Visualizzazione di un Container basilare per a) Android e b) iOS (click per ingrandire)


Visualizzazione di un Container basilare per a) Android e b) iOS

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)


Visualizzazione del Container con le dimensioni impostate e del precedente Container per a) Android e b) iOS

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)


Visualizzazione del Container con le proprietà padding e margin definite e dei precedenti Container per a) Android e b) iOS

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)


Visualizzazione del Containter con allineamento in basso a sinistra del widget figlio e dei precedenti Container per a) Android e b) iOS

Complichiamo leggermente lo scenario e definiamo un Container che definisca le proprietà:

  • constraint creando un BoxConstaint;
  • padding tramite il costruttore all() di EdgeInsets;
  • decoration utilizzando l’oggetto ShapeDecoration.

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)


Visualizzazione del Container dai bordi arrotondati e dei precedenti Container per a) Android e b) iOS

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)


Visualizzazione del Container ruotato e dei precedenti Container per a) Android e b) iOS

Il codice di questa lezione è disponibile al seguente link.

Ti consigliamo anche