Extensions no Dart: Escrevendo Código Mais Limpo e Reutilizável
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 welitonEntendendo o this
Dentro de uma Extension, o this representa a instância que está utilizando aquele método.
No exemplo anterior:
'weliton'.batataO 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 SouzaEssa 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 SousaExtensions 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.widthCom Extension:
context.widthAlé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.