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

Stack Widget per la sovrapposizione di widget

Impariamo a creare layout con widget sovrapposti, tramite l'uso dello Stack widget fornito da Flutter, framework per creare app mobile multipiattaforma.
Impariamo a creare layout con widget sovrapposti, tramite l'uso dello Stack widget fornito da Flutter, framework per creare app mobile multipiattaforma.
Link copiato negli appunti

Nella precedente lezione, abbiamo mosso i primi passi nella definizione di multi-child layout attraverso l’uso di Row e Column. Questi però non sono gli unici widget definiti da Flutter per creare interfacce complesse. Tra essi, infatti, c’è lo Stack Widget che permette la sovrapposizione di più widget su layer differenti.

In questa lezione ci focalizzeremo proprio su questo componente attraverso alcuni esempi pratici, per meglio cogliere gli aspetti fondamentali e gli utilizzi di questo widget.

Stack Widget

Analogamente a quanto visto finora, lo Stack widget può contenere molteplici widget figli che si trovano, a differenza dei widget Row e Column, su livelli differenti, componendo una vera e propria pila di elementi. Questo componente è quindi molto utile quando vogliamo sovrapporre diversi widget per creare interfacce nuove e articolate.

In uno Stack widget:

  • il primo widget nella lista dei widget figli è quello di base;
  • i widget figli successivi al primo sono sovrapposti al primo e tra loro, posizionandosi su layer differenti;
  • non è possibile effettuare lo scroll dei widget figli;
  • è possibile ritagliare i nodi figli che superano il box di rendering definito evitando effetti visivi poco gradevoli.

Uno degli aspetti particolari di questo questo widget è la possibilità di collocare i widget figli in una specifica posizione in base alla definizione o meno del widget Positioned. Infatti, tutti i widget figli che devono essere collocati in una data posizione all’interno dello Stack widget devono essere incapsulati dal widget Positioned. Quest'ultimo definisce sei proprietà:

Proprietà Tipo Accettato Descrizione
bottom double rappresenta la distanza tra il bordo inferiore del nodo figlio e la parte inferiore dello Stack
child Widget il widget da inserire nello Stack
height double l’altezza del nodo figlio
left double rappresenta la distanza tra il bordo sinistro del nodo figlio e il lato sinistro dello Stack
right double rappresenta la distanza tra il il bordo destro del nodo figlio e il lato destro dello Stack
top double rappresenta la distanza tra il bordo superiore del nodo figlio e la parte superiore dello Stack
width double la larghezza del nodo figlio

Attraverso queste proprietà è facile definire la posizione dei widget che devono essere messi nella pila. Tutti i widget racchiusi in un Positioned widget e collocati in uno Stack sono detti positioned child.

È bene precisare che non è obbligatorio usare il Positioned widget, dal momento che lo Stack widget è in grado di gestire comunque i propri figli collocandoli attraverso la proprietà alignment, come vedremo negli esempi più avanti. Questi componenti, a differenza dei primi, sono detti non-positioned child.

Infine, indipendentemente dalla posizione dei widget, la dimensione dello Stack dipende dalla dimensione del widget figlio più grande.

Le proprietà

Come tutti i widget che abbiamo visto in queste lezioni, lo Stack widget fornisce poche ma utili proprietà per organizzare la pila di widget da mostrare. Tra queste abbiamo:

Proprietà Tipo Accettato Descrizione
alignment AlignmentGeometry come allineare i non-positioned child
children List<Widget> racchiude la lista di componenti da visualizzare nella pila
fit StackFit come devono essere dimensionati nella pila i non-positioned child. Ciò è reso possibile dall’utilizzo delle proprietà del widget StackFit
overflow Overflow utilizza le proprietà del widget Overflow per gestire il problema di widget figli che fuoriescono dallo Stack. In generale, quando questa proprietà è settata a Overflow.clip, i figli dello Stack widget non possono essere disegnati al di fuori di esso
textDirection TextDirection la direzione del testo

Vediamo adesso qualche esempio pratico di utilizzo per poter sfruttare al meglio gli Stack widget.

Esempi pratici

Per iniziare, creiamo un nuovo progetto come mostrato in una precedente lezione 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 di 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.

Vediamo da subito un semplice esempio di utilizzo immaginando di voler creare un Stack composto da tre diversi Container di diverse dimensioni e senza una specifica posizione. Inseriamo inoltre lo Stack all’interno di un altro Container.

Container(
      margin: EdgeInsets.only(top: 10),
      child: Stack(
        children: <Widget>[
          // Max Size
          Container(
            color: Colors.orange,
            height: 300,
            width: 300,
          ),
          Container(
            color: Colors.yellowAccent,
            height: 200.0,
            width: 200.0,
          ),
          Container(
            color: Colors.greenAccent,
            height: 100.0,
            width: 100.0,
          )
        ],
      )),

Eseguendo l’app, possiamo notare che tutti e tre gli elementi sono posizionati nell’angolo in alto a sinistra della schermata e, grazie alla differenza di dimensioni, è possibile vedere i diversi Container dove quello giallo è posizionato più in basso nello stack, come si evince in figura.

Figura 107. Creazione di un semplice Stack widget composto da tre Container per a) Android e b) iOS (click per ingrandire)Creazione di un semplice Stack widget composto da tre Container per a) Android e b) iOS

Per ovviare a questo problema possiamo sfruttare la proprietà alignment. Come anticipato all’inizio della lezione, questa proprietà ci permette di posizionare lo Stack rispetto al nodo padre. Vediamo un esempio.

Container(
    margin: EdgeInsets.only(top: 10),
    child: Stack(
      alignment: Alignment.centerRight,
      children: <Widget>[
        Container(
          color: Colors.orange,
          height: 300,
          width: 300,
        ),
        Container(
          color: Colors.yellowAccent,
          height: 200.0,
          width: 200.0,
        ),
        Container(
          color: Colors.greenAccent,
          height: 100.0,
          width: 100.0,
        )
      ],
    ),
  ),

Eseguendo l’app, possiamo vedere che impostando la proprietà a Alignment.centerRight lo Stack è centrato rispetto al Container ed è spostato sulla destra.

Figura 108. Utilizzo della proprietà alignment impostata a Alignment.centerRight per a) Android e b) iOS (click per ingrandire)Utilizzo della proprietà alignment impostata a Alignment.centerRight per a) Android e b) iOS

Una volta visto come disporre lo Stack all’interno del widget padre, è quasi naturale chiedersi come disporre i widget figli di Stack affinché assumano una precisa posizione all’interno dello Stack. Per compiere questo spostamento abbiamo due possibilità:

  • utilizzare il widget Align;
  • utilizzare il widget Positioned.

Vediamo come utilizzare entrambe le opzioni.

Prendiamo in considerazione il primo esempio mostrato in questa lezione e modifichiamo le dimensioni dei widget. Prendiamo l’ultimo Container dello Stack e incapsuliamolo all’interno dell’Align widget. Impostiamo inoltre la proprietà alignment a Alignment.topRight per posizionare il nostro elemento in alto a destra nello Stack.

Align(
  alignment: Alignment.topRight,
  child: Container(
    color: Colors.greenAccent,
    height: 100.0,
    width: 100.0,
  )
)

Il risultato di questa modifica è visibile nella seguente immagine.

Figura 109. Utilizzo dell’Align widget per il posizionamento di un elemento nello Stack per a) Android e b) iOS (click per ingrandire)Utilizzo dell’Align widget per il posizionamento di un elemento nello Stack per a) Android e b) iOS

Nonostante l'uso di Align rappresenti una buona soluzione, essa non permette di avere una grande flessibilità, costringendoci a impostare manualmente i margini e la distanza dal bordo superiore e destro del Container. Pertanto è consigliabile utilizzare al suo posto il widget Positioned.

Analogamente a quanto fatto prima, incapsuliamo l’ultimo widget all’interno del widget Positioned e definiamo per questo le proprietà right e top per spostare il Container dai bordi come segue:

Positioned(
    right: 50.0,
    top: 60.0,
    child: Container(
      color: Colors.greenAccent,
      height: 100.0,
      width: 100.0,
    )
  )

Questa modifica ci permette di avere il risultato mostrato nella seguente figura e un maggiore controllo sul posizionamento dell’elemento nello Stack.

Figura 110. Utilizzo del Positioned widget per il posizionamento di un elemento nello Stack (click per ingrandire)Utilizzo del Positioned widget per il posizionamento di un elemento nello Stack

Vediamo invece adesso come utilizzare la proprietà fit dello Stack widget. Definiamo per semplicità un metodo che avrà come parametro un possibile valore per la proprietà fit e restituirà uno Stack widget composto da un solo elemento (per semplificare lo scenario).

Widget _setFitStack(fit) => Stack(
    fit: fit,
    children: [
      Container(
        color: Colors.greenAccent,
        height: 100.0,
        width: 100.0,
      )
    ],
  );

Usiamo quindi questa funzione con i tre possibili valori che abbiamo a disposizione per la proprietà fit, che sono:

Valore Descrizione
loose rende i widget figli più piccoli possibile all’interno dello Stack
expand imposta la dimensione dei widget figli alla più grande possibile
passthrough il comportamento dipende dal nodo padre in cui è definito lo Stack

Invochiamo quindi la funzione all’interno del nostro codice come segue.

Container(
    constraints: BoxConstraints.expand(height: 160),
    child: _setFitStack(StackFit.loose),
  ),
  Container(
    constraints: BoxConstraints.expand(height: 160),
    child: _setFitStack(StackFit.expand),
  ),
  Container(
    color: Colors.black,
    alignment: Alignment.center,
    child: _setFitStack(StackFit.passthrough),
  ),

Come si può osservare, il metodo privato che abbiamo definito viene invocato all’interno di un apposito Container widget di cui vengono impostati dei vincoli di altezza per i primi due widget, mentre per il terzo Container sono state impostate alcune proprietà. Eseguendo quindi l’app, otterremo il seguente risultato.

Figura 111. Esempio di utilizzo della proprietà fit per a) Android e b) iOS (click per ingrandire)Figura 111. Esempio di utilizzo della proprietà fit per a) Android e b) iOS

È interessante notare come nel caso del valore loose vengano mantenute le dimensioni originali del widget figlio del Container, mentre col valore passthrough il comportamento cambia in base alle proprietà definite. Nel caso mostrato, avremo che il Container verde acqua è posto al centro mantenendo le dimensioni originali.

Vediamo infine la proprietà overflow che permette di definire quando lasciare visibile o tagliare i widget figli il cui contenuto fuoriesce dallo Stack.

Definiamo quindi una semplice funzione che, data la proprietà overflow, restituirà un Container con un’altezza massima di 38, che definisce al suo interno uno Stack composto da un Text widget e la proprietà overflow in base al valore passato da parametro.

Widget _setOverflowStack(overflow) => Container(
    margin: EdgeInsets.only(top: 10),
    color: Colors.yellow[800],
    constraints: BoxConstraints.expand(height: 38),
    child: Stack(
      overflow: overflow,
      children: <Widget>[
        Positioned(
          top: 10,
          child: Text(
            "cogito ergo sum, cogito ergo sum, cogito ergo sum, cogito ergo sum,\ncogito ergo sum, cogito ergo sum",
            style: TextStyle(color: Colors.black, fontSize: 15),
          ),
        )
      ],
    ),
  );

Invochiamo quindi il metodo appena scritto passando come parametro il valore Overflow.visible e Overflow.clip, ottenendo il seguente risultato.

Figura 112. Esempio di utilizzo della proprietà overflow per a) Android e b) iOS (click per ingrandire)Esempio di utilizzo della proprietà overflow per a) Android e b) iOS

Come si può notare, nel primo caso il testo fuoriesce dallo Stack e dal Container, mentre nel secondo la scritta che fuoriesce viene tagliata.

Compreso come usare le diverse proprietà, vediamo come creare uno Stack widget di reale utilizzo in un’app. Immaginiamo che la nostra applicazione debba mostrare la foto profilo dell’utente all’interno di un cerchio e che, oltre all’immagine, debba mostrare il nome utente.

Per fare ciò, dovremo creare uno Stack widget in cui andiamo a definire tre diversi widget figli che saranno rispettivamente:

  • un CircleAvatar che conterrà la foto profilo mostrandola all’interno di un cerchio;
  • un Container contenente un Text widget per mostrare il nome dell’utente su uno sfondo scuro ma trasparente in modo da rendere leggibile la scritta senza oscurare l’immagine di sfondo.

Creiamo quindi un semplice metodo generico che, dato in ingresso il percorso dell’immagine e il nome dell’utente, restituirà uno Stack widget come quello poc’anzi descritto.

Widget _buildStack(pic, name) => Stack(
    alignment: const Alignment(0.6, 0.6),
    children: [
      CircleAvatar(
        backgroundImage: AssetImage(pic),
        radius: 100,
      ),
      Container(
        decoration: BoxDecoration(
          color: Colors.black45,
        ),
        child: Text(
          name,
          style: TextStyle(
            fontSize: 20,
            fontWeight: FontWeight.bold,
            color: Colors.white,
          ),
        ),
      ),
    ],
  );

In questo caso, andiamo a invocare il metodo appena creato passando come nome da mostrare e immagine quelli che abbiamo a disposizione nella cartella assets specificata nel pubspec.yaml (vedi lezione 12 per maggiori dettagli). Inoltre, per motivi grafici centriamo il widget come segue.

Center(
    child: _buildStack('girl.jpg', 'Shawna'),
  )

Eseguendo l’app otterremo il seguente risultato.

Figura 113. Utilizzo di uno Stack widget per la visualizzazione di un’immagine e di un nome utente (click per ingrandire)Utilizzo di uno Stack widget per la visualizzazione di un’immagine e di un nome utente

Il codice di questa lezione è disponibile su GitHub.

Ti consigliamo anche