Um guia prático para evitar confusões comuns
Durante o desenvolvimento Flutter, é comum encontrar conceitos que aparentam ter a mesma função, mas possuem diferenças importantes que podem impactar performance e comportamento da aplicação. Neste artigo, vou te explicar alguns deles para que você não erre mais na hora de escolher qual usar no seu projeto.
Tópicos
- Expanded vs Flexible
- MediaQuery.sizeOf vs MediaQuery.of(context).size
- const vs final
- SingleChildScrollView vs ListView
1. Expanded vs Flexible
Ambos são usados para controlar como widgets ocupam espaço em layouts flexíveis, mas têm comportamentos distintos.
Expanded
Expanded(
child: Container(color: Colors.blue, height: 50),
)
Com o Expanded
o widget irá ocupar TODO o espaço que estiver disponível, ignorando o tamanho que você setou pra ele.
Flexible
Flexible(
child: Container(color: Colors.blue, height: 50),
)
Ao contrário do Expanded
, o Flexible
não força o widget a ocupar todo o espaço. Em vez disso, ele mantém o tamanho natural se tiver espaço suficiente, se adapta (encolhe) automaticamente se não houver espaço, e pode crescer além do tamanho natural apenas se usar a propriedade flex
ou se houver múltiplos widgets flexíveis dividindo o espaço.
Exemplo Comparativo:
Row(
children: [
Container(width: 100, height: 50, color: Colors.red),
// Mantém 150px ou encolhe se necessário
Flexible(child: Container(width: 150, color: Colors.blue, height: 50)),
// Ocupa todo espaço restante
Expanded(child: Container(color: Colors.green, height: 50)),
],
)
2. MediaQuery.of(context).size vs MediaQuery.sizeOf(context)
Ambos retornam o tamanho da tela, mas diferem em performance e funcionalidade.
MediaQuery.of(context).size
final screenSize = MediaQuery.of(context).size;
final width = screenSize.width;
final height = screenSize.height;
Esta forma de pegar o tamanho da tela é bem menos eficiente, pois carrega todas as informações que tem dentro do MediaQuery
(informações de orientação, display, acessibilidade, etc), por isso, o ideal é só usar dessa forma quando você precisa de outras informações além do tamanho.
MediaQuery.sizeOf(context)
final screenSize = MediaQuery.sizeOf(context);
final width = screenSize.width;
final height = screenSize.height;
Esta é a forma mais recomendada e eficiente de pegar o tamanho da sua tela, é um método mais otimizado para casos onde são necessários apenas o tamanho, sem carregar todo o restante das informações.
Além de ser mais eficiente em termos de processamento, o MediaQuery.sizeOf(context)
também evita rebuilds desnecessários do widget quando outras propriedades do MediaQuery mudam (como orientação ou brilho da tela).
O
MediaQuery.sizeOf(context)
só está disponível a partir da versão 3.10 do Flutter
Quando usar cada um:
// Para apenas tamanho da tela
final size = MediaQuery.sizeOf(context);
// Quando precisa de outras informações
final mediaQuery = MediaQuery.of(context);
final size = mediaQuery.size;
final padding = mediaQuery.padding;
final brightness = mediaQuery.platformBrightness;
3. const vs final
Ambos criam variáveis imutáveis, mas diferem em quando o valor é determinado.
const - Valor conhecido em tempo de compilação
const pi = 3.14159;
const appName = "Meu App";
const defaultColors = [Colors.red, Colors.blue];
Com o const
, o valor da variável já é conhecido na hora que você escreve o código. É como se o Flutter já soubesse exatamente o que vai ser armazenado antes mesmo de rodar o app. O const
é mais eficiente pois cria um único objeto na memória que é reutilizado.
final - Valor determinado em tempo de execução
final now = DateTime.now();
final userName = getUserName();
final randomNumber = Random().nextInt(100);
Com o final
, o valor só é definido quando o código está rodando. É útil quando você precisa calcular algo ou buscar informações que só existem na hora da execução. O valor pode vir de funções, cálculos ou operações que só acontecem quando o app roda. Cada instância final
cria seu próprio espaço na memória.
Exemplo prático:
class UserProfile {
static const defaultAvatar = "assets/default_avatar.png"; // Sempre o mesmo
final String userId; // Diferente para cada usuário
final DateTime createdAt; // Definido na criação do objeto
UserProfile({required this.userId}) : createdAt = DateTime.now();
}
4. SingleChildScrollView vs ListView
Ambos permitem rolagem, mas diferem significativamente em performance e uso de memória.
SingleChildScrollView - Para conteúdo limitado
SingleChildScrollView(
child: Column(
children: [
Container(height: 200, color: Colors.red),
Container(height: 200, color: Colors.blue),
Container(height: 200, color: Colors.green),
],
),
)
O SingleChildScrollView
carrega todo o conteúdo na memória de uma só vez, mesmo as partes que não estão visíveis na tela. É adequado quando você tem poucos widgets ou conteúdo pequeno, sendo simples de implementar mas podendo consumir muita memória se usado com muito conteúdo.
ListView - Para listas grandes
ListView.builder(
itemCount: 1000,
itemBuilder: (context, index) {
return ListTile(
title: Text('Item $index'),
subtitle: Text('Descrição do item $index'),
);
},
)
O ListView
é muito mais inteligente pois carrega apenas os widgets que estão visíveis na tela (lazy loading). Conforme você rola, ele vai criando os novos itens e destruindo os que saem de vista, sendo extremamente eficiente para listas grandes e gerenciando a memória automaticamente.
Em resumo...
Situação | Opção Recomendada | Motivo |
---|---|---|
Layout flexível que pode não ocupar todo espaço | Flexible | Respeita tamanho natural |
Layout que deve ocupar todo espaço disponível | Expanded | Força expansão |
Apenas obter tamanho da tela | MediaQuery.sizeOf | Mais performático |
Valores conhecidos no código | const | Melhor performance |
Valores calculados em runtime | final | Flexibilidade necessária |
Lista com poucos itens | SingleChildScrollView | Simplicidade |
Lista com muitos itens | ListView | Eficiência de memória |
Extra: Agora sabe uma coisa que até parece diferente mas é igual?
Resposta: Navigator.pop(context) e Navigator.of(context).pop()
Isso mesmo, os dois fazem exatamente a mesma coisa na prática! O Navigator.pop(context)
é apenas um atalho para Navigator.of(context).pop()
. O ideal é usar Navigator.pop(context) diretamente quando você só vai fazer uma operação, mas se você vai usar o navigator em vários lugares, aí sim faz sentido armazenar o Navigator.of(context) em uma variável:
final navigator = Navigator.of(context);
navigator.pop();
navigator.pushNamed('/home');
navigator.pushReplacementNamed('/profile');
Dessa forma você evita ficar repetindo Navigator.of(context)
várias vezes no mesmo código.
Conclusão
Entender essas diferenças permite escolhas mais conscientes que resultam em código mais performático, manutenível e adequado para cada situação específica. A regra geral é sempre optar pela solução mais simples que atende aos requisitos, evoluindo para alternativas mais complexas apenas quando necessário.
Espero que tenha gostado do conteúdo, qualquer dúvida é só deixar sua pergunta, até a próxima :)
Top comments (3)
Ótimo conteúdo, foi bastante esclarecedor e direto ao ponto :D
Muito obrigada Clinton, aguardando seu próximo artigo de Laravel kk
interesting information, thank you