Se há uma coisa pela qual o Flutter é conhecido, é a incrível quantidade de widgets com que ele vem. Todos esses widgets ajudam os desenvolvedores a obter a aparência exata que desejam com o mínimo esforço possível.

Nesta postagem, veremos um widget que todo desenvolvedor de Flutter deve conhecer: o widget Stack.

Por meio do uso eficaz do widget Stack em um aplicativo, podemos comunicar a profundidade aos usuários e criar alguns layouts bastante complexos sem muito trabalho.

Qual é a aparência de um widget Stack como?

Aqui, podemos ver um exemplo de que tipo de layout podemos obter com um Stack widget em nosso aplicativo Flutter .

Um exemplo de layout obtido com um Stack (foto de Sarah Dorweller em Unsplash ).

Para este aplicativo, vemos uma imagem no centro e, em seguida, duas outras imagens em cada lado. As imagens à esquerda e à direita têm um tamanho um pouco menor e são colocadas atrás da imagem no meio.

Basicamente, esses widgets são empilhados uns sobre os outros, dando ao usuário uma noção clara do que nós deseja que eles se concentrem.

Como um widget Stack funciona?

Para demonstrar o que um widget Stack faz, primeiro vamos ver como uma coluna organiza seus filhos. Neste exemplo simples, temos cinco contêineres dispostos com larguras e alturas progressivamente maiores:

Widget build (BuildContext context) {return Scaffold (body: Column (children: [… List.generate (5, (índice)=> Container (largura: índice * 50 + 50, altura: índice * 50 + 50, cor: Color.fromRGBO (índice * 20, índice * 20, índice * 40, 1,0),),). revertido] ,),); }

Este código resulta no seguinte:

Agora, se substituirmos o widget Coluna por um widget Pilha, ele se tornará este:

Em vez dos widgets dispostos no eixo vertical, eles são empilhados uns sobre os outros. Isso é benéfico quando queremos nossos widgets um em cima do outro, e não de cima para baixo ou da esquerda para a direita.

Também podemos ver que os widgets são renderizados de baixo para cima. Em nosso exemplo, o widget maior é renderizado na parte inferior da pilha e o widget menor é renderizado no topo e assim por diante.

Os widgets filhos são alinhados no canto superior esquerdo por padrão, e o A pilha é redimensionada para caber em todos os filhos, o que significa que será tão grande quanto nosso maior widget filho.

Alinhamento e ajuste

Às vezes, se colocarmos um widget menor dentro de um widget maior, é mais agradável esteticamente alinhar todos os filhos ao centro.

Se quisermos alinhar nossos widgets ao centro para apelo visual, podemos alinhar nossos widgets filhos dentro da Pilha ao centro. Para fazer isso, é tão fácil quanto definir a propriedade de alinhamento em nosso Stack para Alignment.center, assim:

Widget build (BuildContext context) {return Scaffold (body: Stack (direction: Alignment.center,//Center filhos nos filhos da pilha: [… List.generate (5, (índice)=> Container (largura: índice * 50 + 50, altura: índice * 50 + 50, cor: Color.fromRGBO (índice * 20, índice * 20, índice * 40, 1,0),),). Revertido,],),); }

Isso centraliza todos os filhos da pilha no centro relativo, da seguinte forma:

Como ainda não centralizamos a pilha, ela permaneceu no canto superior esquerdo. Em vez disso, apenas centralizamos os widgets que estão dentro da pilha.

Também podemos usar o parâmetro de ajuste para definir se nossa pilha deve se expandir para preencher o widget pai ou se deve passar pelo ajuste do filho objetos diretamente para os filhos na pilha.

Em termos gerais, eles se aplicam apenas a cenários de layout mais avançados, portanto, devemos deixar o ajuste como StackFit.loose, que é o padrão.

Também podemos posicionar widgets dentro da própria pilha usando Posicionados. Se adicionarmos um contêiner com um fundo azul, colocar algum texto nele e posicioná-lo na parte inferior central, o widget será organizado de acordo com os limites da pilha.

Nosso código então se torna este:

Widget build (BuildContext context) {return Scaffold (body: Stack (alinhamento: Alignment.center,//Centralizar filhos nos filhos Stack: [… List.generate (5, (índice)=> Container (largura: índice * 50 + 50, altura: índice * 50 + 50, cor: Color.fromRGBO (índice * 20, índice * 20, índice * 40, 1,0),),).reversed,//O segundo filho posiciona o contêiner na parte inferior//da pilha pai. Posicionado (esquerda: 0, direita: 0, inferior: 0, filho: Container (color: Colors.blue.withOpacity (0.8), child: Text (“Yay for LogRocket!”, textAlign: TextAlign.center, style: Theme.of (context).textTheme.headline5!.copyWith (color: Colors.white,),),),)],),); }

Isso nos dá o seguinte resultado, onde os filhos dentro da pilha são centralizados e nosso contêiner é alinhado na parte inferior, de acordo com o preenchimento que especificamos nos parâmetros esquerdo, superior e direito.

Uma demonstração completa do código usado para definir a pilha acima pode ser encontrada aqui .

Comportamento de recorte

Também podemos usar Stack para realizar alguns layouts de ótima aparência de nosso aplicativo sem usar as funções de desenho de nível inferior.

Podemos fazer isso por posicionar nossos widgets fora de nossa pilha usando um widget de posição e, em seguida, especificando um número negativo para a direção apropriada (como inferior ou direita).

Se colocarmos um contêiner fora da pilha, podemos veja que o Stack corta nosso widget transbordando por padrão.

Também podemos dizer à nossa pilha para não cortar os widgets que transbordam, especificando clipBehaviour: Clip.none, caso desejemos que os widgets continuem renderizando fora dos limites da pilha.

Usos práticos de pilha

É ótimo ver caixas coloridas em cima umas das outras, mas quando usaríamos realmente uma pilha em seu aplicativo Flutter?

Empilhamento de widgets no um do outro tem uma variedade de usos, mas duas áreas principais onde eles são usados ​​são ao especificar a posição de um widget dentro de um contêiner ou mostrar outro widget que deve estar em primeiro plano.

Para demonstrar isso, vamos fazer um aplicativo que nos mostra fotos de gatos e nos dá a opção de adicioná-los ou removê-los de nossos favoritos. Também sempre nos mostrará o total de quantos gatos temos em nossa lista de favoritos.

Veja como o produto final se parece:

Nosso aplicativo acima tem um Stack que contém um PageView e um Container. O PageView contém cinco fotos de gatos e uma folha de rosto estilizada, enquanto o Container mostra quantos gatos favoritos existem e dá ao usuário a opção de clicar próximo em vez de deslizar.

O Container também está aninhado em um Widget posicionado para que apareça na parte inferior direita da tela. Ele também tem o preenchimento apropriado, portanto, quando o SnackBar for exibido , não se sobrepõe aos botões.

Como podemos ver, os dois botões e a quantidade total de gatos que favorecemos permanecem visíveis mesmo quando interagimos com o PageView diretamente abaixo:

Stack (filhos: [PageView (onPageChanged: (page) {setState (() {showFavouriteButton=page> 0;});}, controlador: _controller, filhos: [Container (decoração: BoxDecoration (gradiente: LinearGradient (começar: Alinhamento). topLeft, end: Alignment.bottomCenter, cores: [Colors.purple, Colors.deepPurpleAccent,],)), child: Center ( child: Text (“Olhe para estes gatos fantásticos!”, style: Theme.of (context).textTheme.headline3,)),),… catImages.map ((e)=> Image.network (e,) ,)],), Positioned (bottom: 50, right: 0, child: Column (children: [Padding (padding: const EdgeInsets.all (16.0), child: Container (padding: EdgeInsets.all (16), decoração: BoxDecoration (borderRadius: BorderRadius.circular (12), color: Colors.blue), child: Column (children: [Text (“Total Favorite Cats”), Text (favourites.length.toString (),),], ),),), Row (filhos: [Preenchimento (preenchimento: const EdgeInsets.all (8.0), filho: AnimatedOpacity (duração: Duração (milissegundos: 500), opacidade: showFavouriteButton? 1: 0, filho: FloatingActionButton (onPressed: () {setState (() {if (favourites.contains (catImages [_controller.page!.Floor ()-1])) {favourites.remove (catImages [_controller.page!.floor ()-1]); ScaffoldMessenger.of (context).showSnackBar (SnackBar (content: Text (“Você removeu este gato de seus favoritos.”),),);} else {favourites.add (catImages [_controller.page!.floor ()-1]); ScaffoldMessenger.of (context).showSnackBar (SnackBar (content: Text (“Você adicionou este gato aos seus favoritos.”),) ,); }}); }, filho: Icon (Icons.favorite),),),), Padding (padding: const EdgeInsets.all (8.0), filho: FloatingActionButton (onPressed: () {_controller.nextPage (duração: Duração (milissegundos: 500) , curve: Curves.fastOutSlowIn);}, child: Icon (Icon (Icons.navigate_next),),)],),],),)],),

Também vemos que os widgets em primeiro plano, como o botões e o contador de favoritos, respondem aos eventos de toque e não os passam para os widgets abaixo.

Onde não há widgets em primeiro plano, nossos eventos de toque passam para o PageView atrás.

Você pode ver o código completo para este projeto aqui .

Usando IndexedStack

Um parente próximo ao widget Stack é o widget IndexedStack. Este widget é igual ao widget Stack, mas nos permite especificar qual item da pilha realmente queremos mostrar.

Isso o torna uma ótima opção para aplicativos onde queremos mostrar um widget em uma vez, pois mantém o estado de cada criança .

Se tivermos um aplicativo que possui uma tela inicial, uma tela de configurações e uma tela de favoritos, podemos definir o widget atual para ser exibido em nosso método setState e alternar facilmente entre os widgets conforme necessário.

Conclusão

O widget Stack é um widget essencial em qualquer kit de ferramentas de desenvolvedores do Flutter e espero que este artigo tenha ajudado você a começar com ele .