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á


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.

DART
// 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';
TEXT
> 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


3. Operadores Aritméticos e Relacionais

(1) Operadores Aritméticos

▶ Exemplo

TEXT
> 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

DART
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)
}
TEXT
> 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

TEXT
> 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

DART
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
}
TEXT
> 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

TEXT
> 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

DART
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)
}
TEXT
> 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

TEXT
> 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

DART
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
}
TEXT
> 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

TEXT
> 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

DART
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
}
TEXT
> 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.

100%
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()"]
TEXT
> 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

TEXT
> 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

DART
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();
}
TEXT
> 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

TEXT
> 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 ?..

DART
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
}
TEXT
> 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

TEXT
> 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

DART
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)
}
TEXT
> 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

TEXT
> 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

DART
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());
  }
}
TEXT
> 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

DART
// ============================================
// 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}');
}
TEXT
> 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:

TEXT
=== 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 a x = x ?? y.

P: Quando devo usar as em vez de is? R: Prefira is para verificação de tipo, pois o compilador realiza promoção automática de tipo. Use as apenas 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 == quanto hashCode.

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


📝 Exercícios

  1. 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.
  2. 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.
  3. 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.

← Aula Anterior | Próxima Aula →

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%