Operadores do Dart — Da Aritmética ao Cascade e Null Safety
Operadores são os verbos do código — domine-os para fazer os dados "se moverem."
1. O que Você Aprenderá
- Operadores Aritméticos / Relacionais / Lógicos / Bitwise
- Operadores de Atribuição e Atribuição Composta
- Operador cascade
..e?..(uma ferramenta poderosa para encadeamento de métodos) - Operadores Null Safety:
?./??/??= - Operadores de Teste de Tipo: is / is! / as
2. A História Real de um Desenvolvedor
(1) Dor: NullPointerException Derrubando Tarefas em Lote
O DataPipeline do Bob frequentemente crashava ao processar milhões de pedidos porque alguns pedidos careciam de campos opcionais. O código estava repleto de verificações if (value != null), mas uma foi esquecida, causando a interrupção do processamento de 300.000 pedidos e exigindo uma reexecução completa.
(2) A Solução com Operadores Null Safety
O Dart fornece três operadores — ?. (acesso seguro), ?? (coalescência nula) e ??= (atribuição consciente de nulo) — que tornam o tratamento de nulos conciso e seguro.
// Antes: verificação de nulo verbosa
String getCity(Order order) {
if (order.customer != null) {
if (order.customer!.address != null) {
return order.customer!.address!.city;
}
}
return 'Unknown';
}
// Depois: operadores null-safe
String getCity(Order order) =>
order.customer?.address?.city ?? 'Unknown';
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
(3) Benefícios
- Redução de 60% nas linhas de código, tornando a lógica de tratamento de nulos clara à primeira vista.
- O operador cascade
..permite encadeamento de métodos mais fluido. - Operadores de teste de tipo tornam a verificação de tipos mais segura.
3. Operadores Aritméticos e Relacionais
(1) Operadores Aritméticos
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Aritmética Básica
void main() {
int a = 17;
int b = 5;
print(a + b); // 22 - Adição
print(a - b); // 12 - Subtração
print(a * b); // 85 - Multiplicação
print(a / b); // 3.4 - Divisão (retorna double)
print(a ~/ b); // 3 - Divisão inteira
print(a % b); // 2 - Módulo (resto)
// Incremento prefixado e posfixado
int count = 10;
print(count++); // 10 (retorna e depois incrementa)
print(count); // 11
print(++count); // 12 (incrementa e depois retorna)
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Significado | Exemplo | Resultado |
|---|---|---|---|
+ |
Adição | 17 + 5 |
22 |
- |
Subtração | 17 - 5 |
12 |
* |
Multiplicação | 17 * 5 |
85 |
/ |
Divisão (retorna double) | 17 / 5 |
3.4 |
~/ |
Divisão Inteira | 17 ~/ 5 |
3 |
% |
Módulo (Resto) | 17 % 5 |
2 |
(2) Operadores Relacionais
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Operações de Comparação
void main() {
int orders = 1500;
int threshold = 1000;
print(orders > threshold); // true
print(orders < threshold); // false
print(orders >= 1500); // true
print(orders <= 1500); // true
print(orders == 1500); // true
print(orders != 1000); // true
// Comparação de strings é lexicográfica
print('apple' < 'banana'); // true
print('USD' == 'USD'); // true
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
4. Operadores Lógicos e Bitwise
(1) Operadores Lógicos
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Operações Lógicas
void main() {
bool isProduction = true;
bool hasErrors = false;
print(isProduction && !hasErrors); // true - AND + NOT
print(isProduction || hasErrors); // true - OR
print(!isProduction); // false - NOT
// Avaliação de curto-circuito
String? name = null;
// name.length > 0 causaria crash, mas:
print(name != null && name.isNotEmpty); // false (seguro)
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Significado | Curto-Circuito |
|---|---|---|
&& |
AND Lógico | Esquerda falsa -> direita não avaliada |
| ` | ` | |
! |
NOT Lógico | — |
(2) Operadores Bitwise
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Operações Bitwise
void main() {
int flags = 0b1010; // 10 em binário
int mask = 0b1100; // 12 em binário
print(flags & mask); // 8 (0b1000) - AND
print(flags | mask); // 14 (0b1110) - OR
print(flags ^ mask); // 6 (0b0110) - XOR
print(~flags); // -11 (bits invertidos) - NOT
print(flags << 2); // 40 (0b101000) - Deslocamento à esquerda
print(flags >> 1); // 5 (0b0101) - Deslocamento à direita
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
5. Operadores de Atribuição e Atribuição Composta
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Atribuição Composta
void main() {
int total = 0;
double revenue = 0.0;
total += 100; // total = total + 100 = 100
total -= 30; // total = 70
total *= 2; // total = 140
total ~/= 3; // total = 46 (divisão inteira)
revenue += 52500.75; // revenue = 52500.75
// Atribuição consciente de nulo
String? outputPath;
outputPath ??= '/tmp/output'; // Atribuir se nulo
print(outputPath); // /tmp/output
outputPath ??= '/other/path'; // Não atribuído (já não-nulo)
print(outputPath); // /tmp/output
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Equivalente A | Exemplo |
|---|---|---|
+= |
a = a + b |
total += 100 |
-= |
a = a - b |
total -= 30 |
*= |
a = a * b |
total *= 2 |
~/= |
a = a ~/ b |
total ~/= 3 |
??= |
a = a ?? b |
path ??= '/default' |
6. Operador Cascade .. e ?..
(1) Operador Cascade
O operador cascade .. permite realizar múltiplas operações no mesmo objeto sem referenciar repetidamente o nome da variável.
graph TD A[Sistema de Operadores] --> B[Aritméticos + Relacionais + Lógicos] A --> C[Cascade ..] A --> D[Null Safety ?. ?? ??=] A --> E[Teste de Tipo is/as] C --> C1["object..method1()..method2()"]
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Operador Cascade
class DataPipeline {
String name = '';
int batchSize = 0;
bool verbose = false;
final List<String> sources = [];
void start() => print('$name started');
}
void main() {
// Sem cascade - repetitivo
final pipeline1 = DataPipeline();
pipeline1.name = 'Analytics';
pipeline1.batchSize = 50000;
pipeline1.verbose = true;
pipeline1.sources.add('orders.csv');
pipeline1.start();
// Com cascade - fluido e limpo
final pipeline2 = DataPipeline()
..name = 'Analytics'
..batchSize = 50000
..verbose = true
..sources.add('orders.csv')
..start();
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Cascade Null-Safe ?..
class Customer {
String? name;
String? email;
}
void main() {
Customer? customer;
// Cascade null-safe - ignorado se o objeto for nulo
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer); // null (cascade foi ignorado)
customer = Customer();
customer?..name = 'Bob'..email = 'bob@example.com';
print(customer?.name); // Bob
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Significado | Quando o Objeto é Nulo |
|---|---|---|
.. |
Operação cascade | Erro |
?.. |
Cascade null-safe | Ignora todo o cascade |
7. Operadores Null Safety
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: Três Operadores Null Safety
class Address {
final String city;
Address(this.city);
}
class Customer {
final String name;
final Address? address;
Customer(this.name, {this.address});
}
class Order {
final String id;
final Customer? customer;
Order(this.id, {this.customer});
}
void main() {
// ?. - acesso null-safe
Order order = Order('ORD-001');
print(order.customer?.name); // null (seguro)
// print(order.customer.name); // Erro em runtime!
// ?? - coalescência nula
String city = order.customer?.address?.city ?? 'Unknown';
print(city); // Unknown
// ??= - atribuição consciente de nulo
String? outputPath;
outputPath ??= '/tmp/reports';
print(outputPath); // /tmp/reports
outputPath ??= '/other/path';
print(outputPath); // /tmp/reports (inalterado)
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Significado | Exemplo | Resultado |
|---|---|---|---|
?. |
Acesso seguro | obj?.method() |
null ou resultado da chamada |
?? |
Coalescência nula | value ?? default |
valor se não-nulo, senão default |
??= |
Atribuição consciente de nulo | var ??= value |
Atribui se nulo |
8. Operadores de Teste de Tipo
▶ Exemplo
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
: is / is! / as
void main() {
Object value = '1,500.00 USD';
// is - verificação de tipo (retorna bool)
if (value is String) {
print('String length: ${value.length}'); // Tipo promovido!
}
// is! - verificação negativa de tipo
if (value is! int) {
print('Not an integer');
}
// as - conversão de tipo (lança exceção se errado)
String text = value as String; // OK
// int number = value as int; // Erro em runtime!
// Padrão de conversão segura
if (value is String) {
String safe = value; // Sem cast necessário (promovido)
print(safe.toUpperCase());
}
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
| Operador | Significado | Em Caso de Falha |
|---|---|---|
is |
Verificação de tipo | Retorna false |
is! |
Verificação negada de tipo | Retorna true |
as |
Conversão de tipo | Lança TypeError |
9. Exemplo Completo: Processamento de Pedidos do DataPipeline com Operadores
// ============================================
// Processamento de Pedidos do DataPipeline com Operadores
// Demonstra todas as categorias de operadores
// ============================================
class Order {
final String id;
final double amount;
String? discountCode;
double? discountPercent;
Order({required this.id, required this.amount});
// Operadores null-safe para cálculo de desconto
double get discountedAmount =>
amount - (amount * (discountPercent ?? 0));
double get tax => discountedAmount * 0.08;
double get total => discountedAmount + tax;
String formatUSD() => '\$${total.toStringAsFixed(2)} USD';
}
class Report {
final List<Order> orders = [];
var totalRevenue = 0.0;
var orderCount = 0;
void addOrder(Order order) {
orders.add(order);
totalRevenue += order.total; // Atribuição composta
orderCount++;
}
double get averageOrderValue =>
orderCount > 0 ? totalRevenue / orderCount : 0;
bool get isHighVolume => orderCount >= 1000;
String get statusLabel => isHighVolume ? 'High Volume' : 'Normal';
}
void main() {
final report = Report()
..addOrder(Order(id: 'ORD-001', amount: 1500.0)
..discountPercent = 0.1)
..addOrder(Order(id: 'ORD-002', amount: 3250.50)
..discountCode = 'SAVE20'
..discountPercent = 0.2)
..addOrder(Order(id: 'ORD-003', amount: 890.25));
print('=== DataPipeline Order Report ===');
for (final order in report.orders) {
final discount = order.discountPercent != null
? '${(order.discountPercent! * 100).toInt()}%'
: 'None';
print(' ${order.id}: ${order.formatUSD()} (Discount: $discount)');
}
print('\n--- Summary ---');
print('Orders: ${report.orderCount}');
print('Revenue: \$${report.totalRevenue.toStringAsFixed(2)} USD');
print('Average: \$${report.averageOrderValue.toStringAsFixed(2)} USD');
print('Status: ${report.statusLabel}');
// Teste de tipo
print('\nType check: report is ${report.runtimeType}');
print('Is Report: ${report is Report}');
}
> Saída: Execute em seu DartPad local ou com `dart run`. Todos os exemplos do curso Dart são baseados em Dart 3.x / Flutter 3.x; os resultados podem variar ligeiramente com a versão do SDK.
Saída:
=== DataPipeline Order Report ===
ORD-001: $1458.00 USD (Discount: 10%)
ORD-002: $2807.43 USD (Discount: 20%)
ORD-003: $961.47 USD (Discount: None)
--- Summary ---
Orders: 3
Revenue: $5226.90 USD
Average: $1742.30 USD
Status: Normal
Type check: report is Report
Is Report: true
❓ Perguntas Frequentes
P: Qual a diferença entre
/e~/? R:/sempre retorna um double (ex.: 17 / 5 = 3.4), enquanto~/retorna o resultado da divisão inteira (ex.: 17 ~/ 5 = 3). Use~/quando precisar de um resultado inteiro.
P: Qual é o valor de retorno do operador cascade
..? R:..retorna o próprio objeto (não o valor de retorno do último método), permitindo encadeamento contínuo. Esta é uma diferença chave em relação a uma chamada de método regular.
P: Qual a diferença entre
??=e??? R:??é o operador de coalescência nula, que retorna o valor não-nulo.??=é o operador de atribuição consciente de nulo, que só atribui se a variável for nula e retorna o resultado.x ??= yé equivalente ax = x ?? y.
P: Quando devo usar
asem vez deis? R: Prefiraispara verificação de tipo, pois o compilador realiza promoção automática de tipo. Useasapenas quando tem certeza do tipo ou precisa de um cast explícito, pois um cast incorreto lançará uma exceção.
P: Quando usar
?..vs..? R: Use?..quando o objeto pode ser nulo, e..quando tem certeza de que é não-nulo.?..ignora todo o cascade se o objeto for nulo.
P:
==compara referências ou valores? R: O==do Dart usa comparação de referência por padrão (como Java), mas muitos tipos embutidos (String, int, double) sobrescrevem==para comparar valores. Classes customizadas precisam sobrescrever tanto==quantohashCode.
P: Operadores bitwise são comumente usados na prática? R: Não frequentemente, mas são essenciais em cenários como sistemas de permissão (flags), parsing de protocolos de rede e algoritmos de criptografia. O desenvolvimento diário usa principalmente operadores aritméticos e lógicos.
📖 Resumo
- Note a diferença entre
/aritmético (retorna double) e~/(divisão inteira). - Operadores lógicos
&&e||têm propriedades de curto-circuito, úteis para acesso seguro. - O operador cascade
..simplifica o encadeamento de métodos;?..é sua versão null-safe. - A tríade Null Safety:
?.(acesso seguro),??(coalescência nula),??=(atribuição consciente de nulo). - Testes de tipo:
isverifica e promove tipos;asforça uma conversão (lança exceção se inseguro).
📝 Exercícios
- Básico (Dificuldade ⭐): Escreva código usando operadores aritméticos para calcular o valor médio por pedido para 1.500.000 pedidos com um total de 75.000.000 USD. Use o operador de divisão correto.
- Intermediário (Dificuldade ⭐⭐): Use o operador cascade
..para criar e configurar um objeto, definindo pelo menos 4 propriedades e chamando 2 métodos. Depois compare com a versão sem cascade. - Desafio (Dificuldade ⭐⭐⭐): Implemente uma classe
SafeValue<T>usando os operadores?.,??e??=para fornecer acesso seguro a valores, valores padrão e atribuição preguiçosa, garantindo que nenhuma exceção de ponteiro nulo seja lançada durante qualquer operação.