Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124
Physical Address
304 North Cardinal St.
Dorchester Center, MA 02124

Este post foi originalmente publicado no blogue DCM.dev. Para ter uma experiência melhor, especialmente o realce de sintaxe, confira o post original.
Já se passaram mais de 6 anos desde que comecei a desenvolver com Flutter e Dart, e mesmo assim ainda me deparo com funcionalidades ou widgets que não sabia que existiam. É fascinante como uma framework com a qual trabalhamos diariamente ainda nos pode surpreender com a sua profundidade e versatilidade.
Neste artigo, quero partilhar algumas dessas jóias escondidas que descobri, widgets e funcionalidades menos conhecidas que podem simplificar o seu processo de desenvolvimento e dar um toque único às suas aplicações. Além disso, vamos aprofundar a implementação e explorar como são implementados para aprender mais sobre os padrões.
Vamos descobrir alguns desses widgets juntos!
Imagine que você está a construir uma funcionalidade em que certos itens na sua aplicação precisam de um indicador visual arrojado, por exemplo, uma etiqueta “VENDA”, “NOVO” ou “EM DESTAQUE” que chame a atenção. Você poderia seguir o caminho personalizado, mas o Flutter oferece um widget muito mais simples e fácil de usar: o widget Banner.

Isto parece-me familiar, não é?
É verdade! O CheckedModeBanner utiliza este widget para mostrar um banner a dizer “DEBUG” quando está a correr em modo de depuração, e o MaterialApp constrói um por padrão.
class CheckedModeBanner extends StatelessWidget {
const CheckedModeBanner({super.key, required this.child});
final Widget child;
@override
Widget build(BuildContext context) {
Widget result = child;
assert(() {
result = Banner( // <--- That's the Banner widget!
message: 'DEBUG',
textDirection: TextDirection.ltr,
location: BannerLocation.topEnd,
child: result,
);
return true;
}());
return result;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
// LOGIC
}
}
Por baixo do capô, o widget Banner depende do CustomPaint para renderização. O CustomPainter dentro do widget desenha uma faixa diagonal usando um Path.
Vamos dar uma olhada mais de perto:
class _BannerState extends State<Banner> {
BannerPainter? _painter;
// ...
@override
Widget build(BuildContext context) {
// ...
_painter?.dispose();
_painter = BannerPainter(
message: widget.message,
textDirection: widget.textDirection ?? Directionality.of(context),
location: widget.location,
layoutDirection: widget.layoutDirection ?? Directionality.of(context),
color: widget.color,
textStyle: widget.textStyle,
shadow: widget.shadow,
);
return CustomPaint(foregroundPainter: _painter, child: widget.child);
}
// ...
}
O widget CustomPaint aqui renderiza o banner e delega a lógica de desenho para a classe BannerPainter. Esse pintor usa as APIs de tela de baixo nível do Flutter para lidar tanto com a faixa diagonal quanto com o texto que a acompanha. A classe painter vai além com seu método paint, que orquestra tudo, desde rotações até o layout do texto:
class BannerPainter extends CustomPainter {
// …
@override
void paint(Canvas canvas, Size size) {
// …
tela
..translate(_translationX(size.width), _translationY(size.height))
..rotate(_rotation)
..drawRect(_kRect, _paintShadow)
..drawRect(_kRect, _paintBanner);
const double width = _kOffset * 2.0;
layout(minWidth: largura, maxWidth: largura);
Pintor de texto!.pintar(
canvas,
_kRect.topLeft + Offset(
0.0, (_kRect.height - _textPainter!.height) / 2.0,
),
);
}
// …
}
A API do widget Banner é simples como a que se segue:
enum BannerLocation {
topStart,
topEnd,
bottomStart,
bottomEnd,
}
Vejamos agora um exemplo completo:
Banner(
message: 'BETA',
location: BannerLocation.bottomStart,
color: Colors.green,
textStyle: TextStyle(
fontSize: 12,
color: Colors.white,
),
child: Card(
elevation: 4,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text('New Feature: Try Now!'),
),
),
);
Este widget era geralmente simples, mas tem muito a oferecer quando se olha para o código fonte, especialmente em termos de aprender a usar o Custom Painter.
Muito bem, vamos agora dar uma vista de olhos a outro widget, que talvez não tenha ouvido falar.
O widget MetaData no Flutter é um daqueles widgets que muitas vezes passam despercebidos.
À primeira vista, ele pode não parecer grande coisa – ele não renderiza nada visível na tela, nem modifica diretamente o comportamento de seu filho. No entanto, o MetaData anexa metadados personalizados a uma subárvore de widgets. Isso significa que, como desenvolvedores do Flutter, podemos definir informações semânticas ou estruturais adicionais.
Considere o seguinte exemplo:
MetaData(
metaData: 'Custom tag for this widget',
child: Container(
height: 100,
width: 100,
color: Colors.blue,
),
);
Aqui, a propriedade MetaData permite-lhe marcar a subárvore de widgets com qualquer informação arbitrária. Estas informações não afectam a apresentação ou a interação, mas podem ser recuperadas e processadas por sistemas externos. Imagine utilizar estes metadados durante os testes automatizados para identificar widgets através de metadados personalizados ou anexar descrições para análises personalizadas.
Vamos aprofundar um pouco mais e responder em que cenários isto pode ser útil.
MetaData se torna aparente nos cenários de acessibilidade. Enquanto o Flutter fornece um rico suporte de acessibilidade embutido através de widgets como Semantics, há momentos em que você precisa adicionar anotações personalizadas. Usando MetaData, você pode marcar widgets com informações adicionais que ferramentas externas podem interpretar.
MetaData(
metaData: {'role': 'button', 'label': 'Custom Button'},
behavior: HitTestBehavior.translucent,
child: GestureDetector(
onTap: () => print('Button tapped'),
child: Container(
height: 50,
width: 150,
color: Colors.green,
child: Center(child: Text('Click Me')),
),
),
);
Neste exemplo, o campo MetaData contém um mapa com funções e etiquetas personalizadas para o widget.
O widget MetaData também desempenha um papel nas ferramentas de depuração e de desenvolvimento. Por exemplo, se estiver a trabalhar numa hierarquia de IU grande e complexa, pode utilizar metadados para marcar secções específicas da árvore. Esta marcação pode ajudá-lo a localizar interações de widgets ou alterações de estado durante o tempo de execução.
Outra caraterística eficaz dos MetaDados reside na propriedade de comportamento. Essa propriedade define como o widget participa do teste de acerto. Por padrão, o widget MetaData não interfere nos testes de acerto, mas você pode configurá-lo usando um dos valores de HitTestBehavior:
Por exemplo, considere um cenário em que você deseja adicionar metadados a um widget invisível que ainda participa de testes de acerto:
MetaData(
metaData: 'Invisible button',
behavior: HitTestBehavior.opaque,
child: Container(
height: 100,
width: 100,
color: Colors.transparent,
),
);
O objetivo do widget RepaintBoundary é limitar a área da tela que precisa ser redesenhada quando ocorrem atualizações visuais, reduzindo repinturas desnecessárias, o que melhora drasticamente o desempenho de um aplicativo, especialmente em cenários com elementos de interface do usuário complexos ou que mudam com frequência.
O widget RepaintBoundary funciona criando uma nova camada de composição no pipeline de renderização do Flutter. As camadas são uma parte essencial do modelo de renderização do Flutter, permitindo que partes da cena sejam desenhadas independentemente. Eu falei sobre o pipeline de renderização no Flutter extensivamente no meu livro FlutterEngineering.io.
Quando um RepaintBoundary é adicionado a um widget, esse widget e a sua subárvore são isolados na sua própria camada de composição. Isto garante que apenas essa camada tem de ser redesenhada quando ocorre uma atualização visual na subárvore, e não o ecrã inteiro.

RepaintBoundary(
child: Container(
width: 100,
height: 100,
color: Colors.blue,
child: Text('Optimized Painting'),
),
);
O objetivo do widget IntrinsicHeight consiste em resolver situações em que as alturas variáveis podem criar desequilíbrios visuais ou inconsistências na interface do utilizador.
O widget IntrinsicHeight pode parecer um widget de nicho, mas o seu valor torna-se óbvio quando se trabalha com layouts dinâmicos em que a manutenção da proporcionalidade é essencial. Deixe-me dar-lhe um exemplo: imagine uma fila de widgets que contêm texto, ícones ou outro conteúdo de diferentes comprimentos. Sem intervenção, a altura de cada widget filho seria determinada de forma independente, o que poderia levar a um desalinhamento.
Considere o código abaixo:
IntrinsicHeight(
child: Row(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Container(
width: 100,
color: Colors.blue,
child: Text('Short text'),
),
Container(
width: 100,
color: Colors.red,
child: Text('This is a much longer piece of text'),
),
],
),
);
Neste exemplo, o widget IntrinsicHeight assegura que ambos os contentores na linha se estendem até à mesma altura, correspondendo ao filho mais alto. Este efeito é conseguido calculando a altura intrínseca de cada widget filho e alinhando-os em conformidade. Sem o IntrinsicHeight, as alturas seriam determinadas de forma independente, levando a um layout visualmente desequilibrado.

O widget IntrinsicWidth no Flutter dimensiona seu filho para a largura intrínseca máxima do filho. É particularmente útil em cenários onde a largura ilimitada está disponível, e um widget filho poderia se expandir infinitamente. Em vez de esticar, o filho é limitado à sua largura intrínseca, criando um layout mais adequado.

Além disso, envolver uma coluna numa IntrinsicWidth garante que todos os seus filhos têm a mesma largura que o filho mais largo, permitindo um alinhamento consistente.
Se eu quiser mencionar as principais caraterísticas e comportamentos deste widget, posso categorizá-los em dois:
IntrinsicWidth(
child: Row(
children: [
Container(
height: 50,
color: Colors.blue,
child: Text('Short'),
),
Container(
height: 50,
color: Colors.red,
child: Text('Much longer text'),
),
],
),
);
Neste artigo, exploramos vários widgets Flutter menos conhecidos, mas eficazes, como AnimatedModalBarrier, FittedBox, SemanticsDebugger e muito mais. Esses widgets não apenas aprimoram a funcionalidade do seu aplicativo, mas também simplificam o processo de desenvolvimento. Também é bom ver o que está por trás desses widgets e aprender com eles para aplicar em nossas aplicações Flutter.
Isso é só o começo. Flutter e Dart estão cheios de jóias escondidas esperando para serem descobertas. Fiquem atentos ao próximo artigo, que irei revelar nas próximas 10 funcionalidades desconhecidas ou menos usadas em Dart e Flutter. Lembrem-se de subscrever a nossa newsletter, YouTube, ou redes sociais para receber as últimas actualizações.