Guia abrangente do Dart e Flutter – Dominando os “Extension Methods”

Tempo de leitura: 4 minutes

Bem-vindo ao mundo do desenvolvimento do Flutter! Este guia se aprofunda em um recurso poderoso do Dart, a linguagem que alimenta o Flutter: métodos de extensão.

Os métodos de extensão permitem que você adicione funcionalidade a classes e bibliotecas existentes sem modificar o código original. Isso torna seu código mais limpo, mais conciso e mais fácil de manter, especialmente ao trabalhar com bibliotecas de terceiros.

 

O que são métodos de extensão?

Os métodos de extensão são uma maneira de aumentar as bibliotecas existentes, fornecendo funcionalidades adicionais que podem não estar presentes no código original. Ao usar a API de outra pessoa ou contribuir para uma biblioteca amplamente usada, modificar a API pode ser impraticável. Os métodos de extensão vêm em seu socorro, permitindo que você amplie os recursos das classes existentes.

 

Visão geral

Considere o seguinte cenário em que você deseja analisar uma cadeia de caracteres em um número inteiro:

int.parse('42');

Para tornar isso mais conveniente, você pode criar um método de extensão na class String:

import 'string_apis.dart';

print('42'.parseInt());

Neste exemplo, o método parseInt é adicionado à classe String usando uma extensão chamada NumberParsing.

 

Uso de métodos de extensão

O uso de métodos de extensão é simples. Importe a biblioteca que contém a extensão e use-a como um método normal:

import 'string_apis.dart';

print('42'.padLeft(5)); // Usando um método String.
print('42'.parseInt()); // Usando um método de extensão.

Lembre-se de que os métodos de extensão são resolvidos em relação ao tipo estático do receptor.

 

Tipos estáticos e dinâmicos

Os métodos de extensão não funcionam com variáveis do tipo dynamic. Eles são resolvidos em relação ao tipo estático do receptor. Por exemplo:

dynamic d = '2';

// A linha a seguir resultará em uma exceção de tempo de execução.
print(d.parseInt());

No entanto, usando a inferência de tipo do Dart, os métodos de extensão funcionam quando o tipo de variável é inferido:

var v = '2';

print(v.parseInt()); // Saida: 2

Conflitos de API

Lidar com conflitos é essencial quando várias extensões ou interfaces entram em conflito. Há duas soluções principais:

  1. Usar show ou hide na importação:
import 'string_apis.dart';

// Importação e ocultação de extensões conflitantes.
import 'string_apis_2.dart' hide NumberParsing2;
print('42'.parseInt());
  1. Aplicando extensões de forma explícita:
import 'string_apis.dart';
import 'string_apis_2.dart';

// Aplicar extensões explicitamente.
print(NumberParsing('42').parseInt());
print(NumberParsing2('42').parseInt());

Quando surgirem conflitos devido ao mesmo nome, use prefixos:

import 'string_apis.dart';
import 'string_apis_3.dart' as rad;

print(NumberParsing('42').parseInt());
print(rad.NumberParsing('42').parseInt());
print('42'.parseNum());

Implementação de métodos de extensão

A criação de uma extensão envolve a especificação do nome da extensão, o tipo de destino e os membros. Esta é a sintaxe básica:

extension <extension name>? on <type> {
  (<member definition>)*
}

Por exemplo, estender a classe String:

extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }

  double parseDouble() {
    return double.parse(this);
  }
}

Os membros podem incluir métodos, getters, setters ou operadores. As extensões também podem ter campos estáticos e métodos auxiliares.

 

Extensões sem nome

As extensões sem nome são visíveis apenas na biblioteca declarante e não podem ser aplicadas explicitamente para resolver conflitos de API:

extension on String {
  bool get isBlank => trim().isEmpty;
}

Observação: os membros estáticos das extensões sem nome só podem ser invocados dentro da declaração da extensão.

 

Implementação de extensões genéricas

As extensões podem ser genéricas, permitindo que você estenda os tipos incorporados com parâmetros de tipo:

extension MyFancyList<T> on List<T> {
  int get doubleLength => length * 2;
  List<T> operator -() => reversed.toList();
  List<List<T>> split(int at) => [sublist(0, at), sublist(at)];
}

Neste exemplo, o tipo T é vinculado com base no tipo estático da lista.

Conclusão

O domínio dos métodos de extensão no Dart oferece uma habilidade valiosa para aprimorar o código sem alterar as bibliotecas existentes. Este guia teve como objetivo tornar o conceito acessível aos iniciantes, permitindo que eles aproveitem esse poderoso recurso em sua jornada de desenvolvimento do Dart e do Flutter. Sinta-se à vontade para explorar mais e experimentar os métodos de extensão para simplificar seu código!

 

Perguntas frequentes

1. O que são métodos de extensão no Dart?
Os métodos de extensão no Dart são uma forma de adicionar novas funcionalidades às classes existentes sem modificar o código. Eles permitem que os desenvolvedores estendam as bibliotecas, tornando mais conveniente trabalhar com APIs de terceiros ou contribuir com bibliotecas amplamente utilizadas.

2. Como faço para usar os métodos de extensão?
Para usar os métodos de extensão, importe a biblioteca que contém a extensão e, em seguida, use os métodos como se fossem métodos regulares. Por exemplo:

import 'string_apis.dart';

print('42'.parseInt());

Esse código usa o método de extensão parseInt na classe String.

3. Posso usar métodos de extensão com tipos dinâmicos?
Não, os métodos de extensão não podem ser invocados em variáveis do tipo dynamic. Eles são resolvidos em relação ao tipo estático do receptor. No entanto, a inferência de tipo do Dart permite que você use métodos de extensão quando o tipo da variável é inferido.

4. Como posso lidar com conflitos ao usar várias extensões?
Os conflitos podem ser resolvidos com o uso show ou hide ao importar extensões conflitantes. Como alternativa, você pode aplicar extensões explicitamente, fornecendo uma referência clara à extensão desejada. Os prefixos também podem ser usados quando as extensões têm o mesmo nome.

5. Há restrições quanto ao uso de extensões sem nome?
Sim, as extensões sem nome são visíveis somente na biblioteca em que foram declaradas. Elas não podem ser aplicadas explicitamente para resolver conflitos de API fora da biblioteca que as declara. Além disso, os membros estáticos de extensões sem nome só podem ser invocados dentro da declaração da extensão.