Extensions no Dart: Escrevendo Código Mais Limpo e Reutilizável

Dart Extensions

O que são Extensions?

Durante o desenvolvimento é muito comum precisarmos criar funções utilitárias para manipular Strings, números, datas ou até mesmo classes do próprio Flutter.

Antes das Extensions, normalmente criávamos classes auxiliares como:

class StringUtils {
  static bool isCPF(String value) {
    return value == '123.123.123-12';
  }
}

E então utilizávamos:

final valid = StringUtils.isCPF(cpf);

Embora funcione, essa abordagem torna o código mais verboso e menos intuitivo.

Com as Extensions podemos adicionar novos comportamentos diretamente em qualquer classe, inclusive nas classes nativas do Dart.


Criando a Primeira Extension

Vamos criar uma extensão simples para a classe String.

extension StringExt on String {
  String get batata => 'batata $this';
}

Agora qualquer String da aplicação passa a possuir a propriedade batata.

Exemplo:

void main() {
  print('weliton'.batata);
}

Resultado:

batata weliton

Entendendo o this

Dentro de uma Extension, o this representa a instância que está utilizando aquele método.

No exemplo anterior:

'weliton'.batata

O valor de this será:

'weliton'

Isso permite acessar e manipular o valor original da variável da mesma forma que fazemos dentro de uma classe comum.


Extensions com Métodos

Além de getters, podemos criar métodos completos.

extension StringExt on String {
  String prefix(String value) {
    return '$value $this';
  }
}

Uso:

print('Souza'.prefix('Weliton'));

Resultado:

Weliton Souza

Essa abordagem permite criar APIs extremamente intuitivas.


Criando Validações

Uma das utilizações mais comuns é encapsular validações.

Por exemplo:

extension StringExt on String {
  bool get isCPF {
    return this == '123.123.123-12';
  }
}

Utilização:

if (cpf.isCPF) {
  print('CPF válido');
}

Naturalmente uma validação real de CPF possui um algoritmo mais complexo, porém o exemplo demonstra como Extensions tornam o código muito mais legível.


Extensions em Objetos de Domínio

Extensions não servem apenas para tipos primitivos.

Suponha uma entidade:

class User {
  final String firstName;
  final String lastName;
 
  User(this.firstName, this.lastName);
}

Podemos adicionar comportamentos sem alterar a classe original:

extension UserExt on User {
  String get fullName {
    return '$firstName $lastName';
  }
}

Uso:

final user = User('Weliton', 'Sousa');
 
print(user.fullName);

Resultado:

Weliton Sousa

Extensions no Flutter

No Flutter as Extensions são utilizadas extensivamente para simplificar código repetitivo.

Um exemplo bastante comum é criar atalhos para acessar o tamanho da tela.

extension SizesExt on BuildContext {
  double get width => MediaQuery.of(this).size.width;
 
  double get height => MediaQuery.of(this).size.height;
}

Sem Extension:

MediaQuery.of(context).size.width

Com Extension:

context.width

Além de reduzir código, a leitura se torna muito mais agradável.


Navegação com Extensions

Outro exemplo extremamente comum é simplificar a navegação.

extension NavigationExt on BuildContext {
  void push(Widget page) {
    Navigator.of(this).push(
      MaterialPageRoute(
        builder: (_) => page,
      ),
    );
  }
}

Agora podemos navegar utilizando:

context.push(
  const HomePage(),
);

Em vez de escrever:

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (_) => const HomePage(),
  ),
);

Essa pequena abstração elimina dezenas de linhas repetidas ao longo do projeto.


Quando Utilizar Extensions?

Extensions são ideais para:

  • Formatação de Strings.
  • Validações.
  • Conversões.
  • Manipulação de datas.
  • Utilitários para Flutter.
  • Navegação.
  • Helpers para BuildContext.
  • Regras reutilizáveis.

Quando Evitar?

Nem toda lógica deve virar uma Extension.

Evite utilizar quando:

  • A lógica depende de muitos serviços externos.
  • Existe alteração de estado complexa.
  • O comportamento não pertence naturalmente ao objeto.

Nesses casos uma classe de serviço geralmente é mais adequada.


Conclusão

Extensions são um dos recursos mais úteis do Dart moderno. Elas permitem adicionar funcionalidades a classes existentes sem herança ou modificações diretas, tornando o código mais limpo, legível e reutilizável.

No Flutter elas são especialmente valiosas para simplificar operações comuns envolvendo BuildContext, navegação, validações e formatação de dados.

Depois que você começa a utilizá-las corretamente, rapidamente percebe como elas ajudam a construir APIs mais elegantes e um código muito mais agradável de manter.