Flutter Layout Cheat Sheet
Você precisa de amostras de layout simples para o Flutter?
Apresento a você meu conjunto de trechos de código de layout do Flutter. Vou mantê-lo curto, doce e simples, com muitos exemplos visuais.
Ainda assim, é um trabalho em andamento – o catálogo de amostras vai crescer. Vou me concentrar mais no uso de widgets do Flutter em vez de mostrar os componentes (a Galeria do Flutter é ótima para isso!).
Se você tiver um problema com o “layout” do seu Flutter ou quiser compartilhar seus trechos com outras pessoas, envie uma mensagem!
Conteudo
Row e Column
MainAxisAlignment
Row /*ou Column*/( mainAxisAlignment: MainAxisAlignment.start, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( mainAxisAlignment: MainAxisAlignment.end, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( mainAxisAlignment: MainAxisAlignment.spaceAround, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Você deve usar CrossAxisAlignment.baseline se precisar que a linha de base de um texto diferente seja alinhada.
Row( crossAxisAlignment: CrossAxisAlignment.baseline, textBaseline: TextBaseline.alphabetic, children: <Widget>[ Text( 'Baseline', style: Theme.of(context).textTheme.display3, ), Text( 'Baseline', style: Theme.of(context).textTheme.body1, ), ], ),
CrossAxisAlignment
Row /*ou Column*/( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 200), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( crossAxisAlignment: CrossAxisAlignment.end, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 200), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 200), Icon(Icons.star, size: 50), ], ),
MainAxisSize
Row /*ou Column*/( mainAxisSize: MainAxisSize.max, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
Row /*ou Column*/( mainAxisSize: MainAxisSize.min, children: <Widget>[ Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
IntrinsicWidth e IntrinsicHeight
Deseja que todos os widgets dentro de Linha ou Coluna sejam tão altos/largos quanto o widget mais alto/largo? Não procure mais!
Caso você tenha esse tipo de layout:
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('IntrinsicWidth')), body: Center( child: Column( children: <Widget>[ ElevatedButton( onPressed: () {}, child: Text('Short'), ), ElevatedButton( onPressed: () {}, child: Text('A bit Longer'), ), ElevatedButton( onPressed: () {}, child: Text('The Longest text button'), ), ], ), ), ); }
Mas você gostaria de ter todos os botões tão largos quanto o mais largo, basta usar
IntrinsicWidth
:
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('IntrinsicWidth')), body: Center( child: IntrinsicWidth( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ ElevatedButton( onPressed: () {}, child: Text('Short'), ), ElevatedButton( onPressed: () {}, child: Text('A bit Longer'), ), ElevatedButton( onPressed: () {}, child: Text('The Longest text button'), ), ], ), ), ), ); }
Caso você tenha um problema semelhante, mas gostaria de ter todos os widgets tão altos quanto o mais alto, basta usar uma combinação dos widgets IntrinsicHeight e Row.
Stack
Perfeito para sobrepor Widgets uns sobre os outros
@override Widget build(BuildContext context) { Widget main = Scaffold( appBar: AppBar(title: Text('Stack')), ); return Stack( fit: StackFit.expand, children: <Widget>[ main, Banner( message: "Top Start", location: BannerLocation.topStart, ), Banner( message: "Top End", location: BannerLocation.topEnd, ), Banner( message: "Bottom Start", location: BannerLocation.bottomStart, ), Banner( message: "Bottom End", location: BannerLocation.bottomEnd, ), ], ); }
Com seus próprios Widgets, você precisa colocá-los no Positioned Widget
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Stack')), body: Stack( fit: StackFit.expand, children: <Widget>[ Material(color: Colors.yellowAccent), Positioned( top: 0, left: 0, child: Icon(Icons.star, size: 50), ), Positioned( top: 340, left: 250, child: Icon(Icons.call, size: 50), ), ], ), ); }
Se você não quiser adivinhar os valores superior/inferior, pode usar o LayoutBuilder para recuperá-los
Widget build(BuildContext context) { const iconSize = 50; return Scaffold( appBar: AppBar(title: Text('Stack with LayoutBuilder')), body: LayoutBuilder( builder: (context, constraints) => Stack( fit: StackFit.expand, children: <Widget>[ Material(color: Colors.yellowAccent), Positioned( top: 0, child: Icon(Icons.star, size: iconSize), ), Positioned( top: constraints.maxHeight - iconSize, left: constraints.maxWidth - iconSize, child: Icon(Icons.call, size: iconSize), ), ], ), ), ); }
Expanded
Expanded funciona com layout Flex \ Flexbox e é ótimo para distribuir espaço entre vários itens.
Row( children: <Widget>[ Expanded( child: Container( decoration: const BoxDecoration(color: Colors.red), ), flex: 3, ), Expanded( child: Container( decoration: const BoxDecoration(color: Colors.green), ), flex: 2, ), Expanded( child: Container( decoration: const BoxDecoration(color: Colors.blue), ), flex: 1, ), ], ),
ConstrainedBox
Por padrão, a maioria dos widgets ocupará o mínimo de espaço possível:
Card(child: const Text('Hello World!'), color: Colors.yellow)
ConstraindBox
permite que um widget use o espaço restante conforme desejado.
ConstrainedBox( constraints: BoxConstraints.expand(), child: const Card( child: const Text('Hello World!'), color: Colors.yellow, ), ),
Usando BoxConstraints
, você especifica quanto espaço um widget pode ter — você especifica min/max de height/width.
BoxConstraints.expand
usa uma quantidade infinita (todo o disponível) de espaço, a menos que seja especificado:
ConstrainedBox( constraints: BoxConstraints.expand(height: 300), child: const Card( child: const Text('Hello World!'), color: Colors.yellow, ), ),
E é o mesmo que:
ConstrainedBox( constraints: BoxConstraints( minWidth: double.infinity, maxWidth: double.infinity, minHeight: 300, maxHeight: 300, ), child: const Card( child: const Text('Hello World!'), color: Colors.yellow, ), ),
Align
Às vezes, você luta para definir nosso widget para um tamanho adequado – por exemplo, ele é constantemente esticado quando você não deseja:
O exemplo acima acontece por exemplo quando você tem uma Coluna com CrossAxisAlignment.stretch e deseja apenas que o botão não fique esticado:
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Align: without Align')), body: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ Align( child: ElevatedButton( onPressed: () {}, child: const Text('Button'), ), ), ], ), ); }
Sempre que seu widget não atender às restrições que você tenta configurar, primeiro tente envolvê-lo com Align
.
Container
Um dos Widgets mais usados — e por boas razões:
Container como ferramenta de layout
Quando você não especifica a altura e a largura do Container, ele corresponderá ao tamanho de seu filho
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container as a layout')), body: Container( color: Colors.yellowAccent, child: Text("Hi"), ), ); }
Se você quiser esticar o Container para corresponder a seu pai, use double.infinity para as propriedades de altura e largura
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container as a layout')), body: Container( height: double.infinity, width: double.infinity, color: Colors.yellowAccent, child: Text("Hi"), ), ); }
Container como decoration
Você pode usar a color property para afetar o Container
’s background, mas a decoration
e foregroundDecoration
. (Com essas duas propriedades, você pode mudar completamente a aparência do Container
, mas falarei sobre diferentes decorations mais tarde, pois é um tópico bastante extenso) a decorations
é sempre colocada atrás do child,
enquanto o foregroundDecoration
fica em cima do child
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container.decoration')), body: Container( height: double.infinity, width: double.infinity, decoration: BoxDecoration(color: Colors.yellowAccent), child: Text("Hi"), ), ); }
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container.foregroundDecoration')), body: Container( height: double.infinity, width: double.infinity, decoration: BoxDecoration(color: Colors.yellowAccent), foregroundDecoration: BoxDecoration( color: Colors.red.withOpacity(0.5), ), child: Text("Hi"), ), ); }
Container como Transform
Se você não quiser usar o widget Transform
para alterar seu layout, pode usar a propriedade transform
diretamente do Container
Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Container.transform')), body: Container( height: 300, width: 300, transform: Matrix4.rotationZ(pi / 4), decoration: BoxDecoration(color: Colors.yellowAccent), child: Text( "Hi", textAlign: TextAlign.center, ), ), ); }
BoxDecoration
A decoration geralmente é usada em um widget Container para alterar a aparência do container.
imagem: DecorationImage
Coloca uma imagem como background:
Scaffold( appBar: AppBar(title: Text('image: DecorationImage')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( color: Colors.yellow, image: DecorationImage( fit: BoxFit.fitWidth, image: NetworkImage( 'https://flutter.io/images/catalog-widget-placeholder.png', ), ), ), ), ), );
border: Border
Especifica como deve ser a aparência do border do Container.
Scaffold( appBar: AppBar(title: Text('border: Border')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( color: Colors.yellow, border: Border.all(color: Colors.black, width: 3), ), ), ), );
borderRadius: BorderRadius
Permite que os cantos da borda sejam arredondados.
borderRadius
não funciona se o shape
da decoração for BoxShape.circle
Scaffold( appBar: AppBar(title: Text('borderRadius: BorderRadius')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( color: Colors.yellow, border: Border.all(color: Colors.black, width: 3), borderRadius: BorderRadius.all(Radius.circular(18)), ), ), ), );
shape: BoxShape
A decoração da caixa pode ser um retângulo/quadrado ou uma elipse/círculo.
Para qualquer outra forma, você pode usar ShapeDecoration
em vez de BoxDecoration
Scaffold( appBar: AppBar(title: Text('shape: BoxShape')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( color: Colors.yellow, shape: BoxShape.circle, ), ), ), );
boxShadow: List<BoxShadow>
Adiciona sombra ao Container.
Este parâmetro é uma lista porque você pode especificar várias sombras diferentes e mesclá-las.
Scaffold( appBar: AppBar(title: Text('boxShadow: List<BoxShadow>')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( color: Colors.yellow, boxShadow: const [ BoxShadow(blurRadius: 10), ], ), ), ), );
gradient
Existem três tipos de gradientes: LinearGradient
, RadialGradient
e SweepGradient
.
Scaffold( appBar: AppBar(title: Text('gradient: LinearGradient')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( gradient: LinearGradient( colors: const [ Colors.red, Colors.blue, ], ), ), ), ), );
Scaffold( appBar: AppBar(title: Text('gradient: RadialGradient')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( gradient: RadialGradient( colors: const [Colors.yellow, Colors.blue], stops: const [0.4, 1.0], ), ), ), ), );
Scaffold( appBar: AppBar(title: Text('gradient: SweepGradient')), body: Center( child: Container( height: 200, width: 200, decoration: BoxDecoration( gradient: SweepGradient( colors: const [ Colors.blue, Colors.green, Colors.yellow, Colors.red, Colors.blue, ], stops: const [0.0, 0.25, 0.5, 0.75, 1.0], ), ), ), ), );
backgroundBlendMode
backgroundBlendMode
é a propriedade mais complexa de BoxDecoration
.
Ele é responsável por misturar cores/gradientes de BoxDecoration
e qualquer BoxDecoration
que esteja por cima.
Com backgroundBlendMode
você pode usar uma longa lista de algoritmos especificados em BlendMode
enum.
Primeiro, vamos definir BoxDecoration
como foregroundDecoration
que é desenhado em cima do filho do Container
(enquanto a decoration
é desenhada atrás do filho).
Scaffold( appBar: AppBar(title: Text('backgroundBlendMode')), body: Center( child: Container( height: 200, width: 200, foregroundDecoration: BoxDecoration( backgroundBlendMode: BlendMode.exclusion, gradient: LinearGradient( colors: const [ Colors.red, Colors.blue, ], ), ), child: Image.network( 'https://flutter.io/images/catalog-widget-placeholder.png', ), ), ), );
backgroundBlendMode
não afeta apenas o Container
em que está localizado.
backgroundBlendMode
altera a cor de qualquer coisa que esteja acima da árvore de widgets do Container
.
O código a seguir tem um Container
pai que desenha uma image
e um Container
filho que usa backgroundBlendMode
. Ainda assim, você obteria o mesmo efeito de antes.
Scaffold( appBar: AppBar(title: Text('backgroundBlendMode')), body: Center( child: Container( decoration: BoxDecoration( image: DecorationImage( image: NetworkImage( 'https://flutter.io/images/catalog-widget-placeholder.png', ), ), ), child: Container( height: 200, width: 200, foregroundDecoration: BoxDecoration( backgroundBlendMode: BlendMode.exclusion, gradient: LinearGradient( colors: const [ Colors.red, Colors.blue, ], ), ), ), ), ), );
Material
Border com cantos cortados
Scaffold( appBar: AppBar(title: Text('shape: BeveledRectangleBorder')), body: Center( child: Material( shape: const BeveledRectangleBorder( borderRadius: BorderRadius.all(Radius.circular(20)), side: BorderSide(color: Colors.black, width: 4), ), color: Colors.yellow, child: Container( height: 200, width: 200, ), ), ), );
Slivers
SliverFillRemaining
Este Widget é insubstituível quando você deseja centralizar seu conteúdo mesmo que não haja espaço suficiente para ele.
Scaffold( appBar: AppBar(title: Text('SliverFillRemaining')), body: CustomScrollView( slivers: [ SliverFillRemaining( hasScrollBody: false, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const [ FlutterLogo(size: 200), Text( 'This is some longest text that should be centered' 'together with the logo', textAlign: TextAlign.center, ), ], ), ), ], ), );
Caso não haja espaço suficiente para o conteúdo centralizado, SliverFillRemaining
se tornará rolável:
Se não fosse por SliverFillRemaining
, o conteúdo transbordaria assim:
Preenchendo o espaço restante
Além de ser útil para centralizar seu conteúdo, SliverFillRemaining
preencherá o espaço livre da viewport restante. Para fazer isso, este widget deve ser colocado no CustomScrollView
e precisa ser a última sliver
Caso não haja espaço suficiente, o widget se torna scrollable:
Scaffold( appBar: AppBar(title: Text('SliverFillRemaining')), body: CustomScrollView( slivers: [ SliverList( delegate: SliverChildListDelegate(const [ ListTile(title: Text('First item')), ListTile(title: Text('Second item')), ListTile(title: Text('Third item')), ListTile(title: Text('Fourth item')), ]), ), SliverFillRemaining( hasScrollBody: false, child: Container( color: Colors.yellowAccent, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: const [ FlutterLogo(size: 200), Text( 'This is some longest text that should be centered' 'together with the logo', textAlign: TextAlign.center, ), ], ), ), ), ], ), );
SizedBox
É um dos Widgets mais simples, mas mais úteis
SizedBox como ConstraindBox
SizedBox
pode funcionar de maneira semelhante a ConstraindBox
SizedBox.expand( child: Card( child: Text('Hello World!'), color: Colors.yellowAccent, ), ),
SizedBox como padding
Quando precisar adicionar padding ou margem, você pode escolher os widgets Padding
ou Container
. Mas eles podem ser mais detalhados e menos legíveis do que adicionar um Sizedbox
Column( children: <Widget>[ Icon(Icons.star, size: 50), const SizedBox(height: 100), Icon(Icons.star, size: 50), Icon(Icons.star, size: 50), ], ),
SizedBox como um objeto invisível
Muitas vezes você gostaria de ocultar/mostrar um widget dependendo de um bool
Widget build(BuildContext context) { bool isVisible = ... return Scaffold( appBar: AppBar( title: Text('isVisible = $isVisible'), ), body: isVisible ? Icon(Icons.star, size: 150) : const SizedBox(), ); }
Como SizedBox tem um construtor const, usar const SizedBox() é muito barato**.
** Uma solução mais barata seria usar o widget Opacity e alterar o valor da opacidade para 0,0 . A desvantagem desta solução é que o widget dado seria apenas invisível, ainda ocuparia o espaço.
SafeArea
Em diferentes plataformas, existem áreas especiais como a barra de status no Android ou o Notch no iPhone X que podemos evitar desenhar.
A solução para este problema é o widget SafeArea (exemplo sem/com SafeArea)
Widget build(BuildContext context) { return Material( color: Colors.blue, child: SafeArea( child: SizedBox.expand( child: Card(color: Colors.yellowAccent), ), ), ); }