DEV Community

Cover image for Flutter - Conceitos que parecem iguais mas são diferentes
Adryanne Kelly
Adryanne Kelly

Posted on

Flutter - Conceitos que parecem iguais mas são diferentes

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


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),
)
Enter fullscreen mode Exit fullscreen mode

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),
)
Enter fullscreen mode Exit fullscreen mode

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)), 
    ], 
)
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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];
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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),
    ],
  ),
)
Enter fullscreen mode Exit fullscreen mode

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'),
    );
  },
)
Enter fullscreen mode Exit fullscreen mode

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');
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
clintonrocha98 profile image
Clinton Rocha

Ótimo conteúdo, foi bastante esclarecedor e direto ao ponto :D

Collapse
 
adryannekelly profile image
Adryanne Kelly

Muito obrigada Clinton, aguardando seu próximo artigo de Laravel kk

Collapse
 
batchdata profile image
BatchData

interesting information, thank you