Fluxo de Controle no Dart — Instruções Condicionais e Laços

O fluxo de controle é o cérebro do seu código — ele determina qual caminho seu programa segue e quantas vezes.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) Dor: if-else Aninhado Torna o Código Inmanutenível

Alice estava processando dados de pedidos de e-commerce e escreveu uma estrutura if-else aninhada em três níveis para filtrar e classificar pedidos. A lógica era: primeiro verificar o status do pedido, depois verificar a faixa de valor, e finalmente verificar o método de pagamento. Uma "pirâmide" de código de 300 linhas exigia que ela entendesse toda a lógica a cada modificação. Uma mudança esquecida levou a um erro nos cálculos de desconto de pedidos VIP, resultando em uma perda direta de $15.000 USD.

(2) A Solução: Melhores Práticas de Fluxo de Controle

Usando a switch expression do Dart 3 para substituir o if-else aninhado, e for-in para substituir a iteração baseada em índice, o código foi reduzido de 300 linhas para 80 linhas, tornando a lógica clara e manutenível.

DART
// Antes: pirâmide de if-else aninhado
// Depois: switch expression limpa
String classifyOrder(Order order) => switch ((order.status, order.amount)) {
  ('pending', > 1000) => 'VIP Pending',
  ('pending', _) => 'Normal Pending',
  ('shipped', _) => 'In Transit',
  ('delivered', _) => 'Completed',
  _ => '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 dependendo da versão do SDK.

(3) Benefícios


3. Instruções Condicionais if-else

(1) Sintaxe Básica

▶ 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 dependendo da versão do SDK.

: Uso Básico de if-else

DART
void main() {
  double orderAmount = 1500.0;

  if (orderAmount > 1000) {
    print('VIP order - apply discount');
  } else if (orderAmount > 500) {
    print('Standard order');
  } else {
    print('Small order');
  }
}
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 dependendo da 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 dependendo da versão do SDK.

: if-else como Expressão (Alternativa ao Operador Ternário)

DART
void main() {
  int orderCount = 1500;

  // Operador ternário
  String tier = orderCount >= 1000 ? 'Enterprise' : 'Standard';

  // if-else como expressão (Dart 3)
  String label = if (orderCount >= 1000) 'Enterprise' else 'Standard';

  print('Tier: $tier');
  print('Label: $label');
}
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 dependendo da versão do SDK.
Forma Sintaxe Retorna Valor Versão do Dart
Instrução if if (cond) { ... } Não Todas as versões
Operador Ternário cond ? a : b Sim Todas as versões
Expressão if if (cond) a else b Sim Dart 3.7+

4. switch e switch Expressions

(1) Instrução switch Tradicional

▶ 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 dependendo da versão do SDK.

: Instrução switch

DART
void main() {
  String status = 'shipped';

  switch (status) {
    case 'pending':
      print('Order is waiting for processing');
      break;
    case 'shipped':
      print('Order is in transit');
      break;
    case 'delivered':
      print('Order has been delivered');
      break;
    default:
      print('Unknown status: $status');
  }
}
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 dependendo da versão do SDK.

(2) switch Expressions do Dart 3

▶ 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 dependendo da versão do SDK.

: Switch Expression

DART
// Switch expression - retorna um valor
String getStatusLabel(String status) => switch (status) {
  'pending' => 'Awaiting Processing',
  'shipped' => 'In Transit',
  'delivered' => 'Completed',
  'cancelled' => 'Cancelled',
  _ => 'Unknown Status',
};

// Pattern matching com guardas
String classifyOrder(double amount) => switch (amount) {
  >= 10000 => 'Enterprise',
  >= 1000 => 'Premium',
  > 0 => 'Standard',
  _ => 'Invalid',
};

void main() {
  print(getStatusLabel('shipped'));    // In Transit
  print(classifyOrder(5500));          // Premium
  print(classifyOrder(150));           // Standard
}
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 dependendo da versão do SDK.
Aspecto Instrução switch Switch Expression
Retorna Valor Não Sim
break Obrigatório Não necessário
Sintaxe case x: x =>
Versão do Dart Todas Dart 3+
Verificação de Exaustividade Não Sim (sealed class)

5. Estruturas de Laço

(1) Laço for

▶ 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 dependendo da versão do SDK.

: Laço for Tradicional

DART
void main() {
  // Laço for clássico
  for (int i = 0; i < 5; i++) {
    print('Processing batch ${i + 1}');
  }

  // Contagem regressiva
  for (int i = 10; i > 0; i -= 2) {
    print('Countdown: $i');
  }
}
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 dependendo da versão do SDK.

(2) Laço for-in

▶ 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 dependendo da versão do SDK.

: Travessia for-in

DART
void main() {
  List<String> sources = ['orders.csv', 'products.csv', 'customers.csv'];

  // for-in com List
  for (final source in sources) {
    print('Loading: $source');
  }

  // for-in com Set
  Set<String> uniqueStatuses = {'pending', 'shipped', 'delivered'};
  for (final status in uniqueStatuses) {
    print('Status: $status');
  }

  // for-in com entradas de Map
  Map<String, int> categoryCount = {
    'Electronics': 500,
    'Clothing': 300,
    'Books': 200,
  };
  for (final entry in categoryCount.entries) {
    print('${entry.key}: ${entry.value} products');
  }
}
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 dependendo da versão do SDK.

(3) Laços while e do-while

▶ 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 dependendo da versão do SDK.

: while e do-while

DART
void main() {
  // while - verifica condição primeiro
  int retries = 0;
  bool connected = false;
  while (!connected && retries < 3) {
    print('Connection attempt ${retries + 1}...');
    retries++;
    connected = retries >= 2;  // Sucesso simulado
  }

  // do-while - executa pelo menos uma vez
  int batchSize = 10000;
  int processed = 0;
  do {
    int chunk = (batchSize - processed > 1000) ? 1000 : batchSize - processed;
    print('Processing $chunk records...');
    processed += chunk;
  } while (processed < batchSize);
}
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 dependendo da versão do SDK.
Tipo de Laço Verificação Inicial Execuções Mínimas Caso de Uso
for Antes da entrada 0 Número conhecido de vezes
for-in Antes da entrada 0 Travessia de coleções
while Antes da entrada 0 Condicional
do-while Após a execução 1 Deve executar pelo menos uma vez

6. break, continue e Rótulos

(1) break e continue

▶ 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 dependendo da versão do SDK.

: break e continue

DART
void main() {
  List<double> amounts = [1500, -50, 3200, 0, 890, -100, 2100];

  // continue - pular pedidos inválidos
  int validCount = 0;
  double totalValid = 0;
  for (final amount in amounts) {
    if (amount <= 0) continue;  // Pular inválidos
    validCount++;
    totalValid += amount;
  }
  print('Valid: $validCount, Total: $totalValid USD');

  // break - parar no primeiro erro
  for (final amount in amounts) {
    if (amount < 0) {
      print('Error: Negative amount $amount found!');
      break;
    }
    print('Processing: $amount USD');
  }
}
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 dependendo da 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 dependendo da versão do SDK.

: Controle com Rótulos

DART
void main() {
  // Rótulo para controle de laço aninhado
  outer:
  for (int batch = 0; batch < 3; batch++) {
    for (int record = 0; record < 5; record++) {
      if (record == 2 && batch == 1) {
        print('Critical error at batch $batch, record $record');
        break outer;  // Sair de ambos os laços
      }
      print('Batch $batch, Record $record');
    }
  }
}
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 dependendo da versão do SDK.

7. Cenário do Bob: Filtragem de Dados do DataPipeline

100%
flowchart TD
  A[Dados Brutos] --> B{Condição de Filtro}
  B -->|Pedido Válido| C[Iterar for-in]
  B -->|Dados Inválidos| D[continue Pular]
  C --> E[Cálculo de Agregação]
  E --> F[Resultado de Saída]
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 dependendo da 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 dependendo da versão do SDK.

: Filtragem e Agregação de Dados de Pedidos

DART
void main() {
  List<Map<String, dynamic>> rawOrders = [
    {'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed'},
    {'id': 'ORD-002', 'amount': -50.0, 'status': 'completed'},
    {'id': 'ORD-003', 'amount': 3200.0, 'status': 'pending'},
    {'id': 'ORD-004', 'amount': 0.0, 'status': 'completed'},
    {'id': 'ORD-005', 'amount': 890.0, 'status': 'completed'},
    {'id': 'ORD-006', 'amount': 2100.0, 'status': 'cancelled'},
  ];

  double totalRevenue = 0;
  int completedCount = 0;
  Map<String, double> revenueByStatus = {};

  for (final order in rawOrders) {
    final amount = order['amount'] as double;
    final status = order['status'] as String;

    // Pular pedidos inválidos
    if (amount <= 0) continue;

    // Pular pedidos cancelados
    if (status == 'cancelled') continue;

    // Agregar receita por status
    revenueByStatus[status] = (revenueByStatus[status] ?? 0) + amount;

    // Contar completados
    if (status == 'completed') {
      completedCount++;
      totalRevenue += amount;
    }
  }

  print('=== DataPipeline Filter Report ===');
  print('Completed orders: $completedCount');
  print('Total revenue: \$${totalRevenue.toStringAsFixed(2)} USD');
  for (final entry in revenueByStatus.entries) {
    print('  ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
  }
}
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 dependendo da versão do SDK.

8. Exemplo Completo: Controlador de Processamento em Lote do DataPipeline

DART
// ============================================
// Controlador de Processamento em Lote do DataPipeline
// Usa todas as estruturas de fluxo de controle
// ============================================

class BatchProcessor {
  final int batchSize;
  int processedCount = 0;
  int skippedCount = 0;
  double totalRevenue = 0;

  BatchProcessor({this.batchSize = 1000});

  String classifyAmount(double amount) => switch (amount) {
    >= 10000 => 'Enterprise',
    >= 1000 => 'Premium',
    > 0 => 'Standard',
    _ => 'Invalid',
  };

  void processBatch(List<Map<String, dynamic>> orders) {
    int batchNum = 0;

    for (int i = 0; i < orders.length; i += batchSize) {
      batchNum++;
      final end = (i + batchSize < orders.length) ? i + batchSize : orders.length;
      final batch = orders.sublist(i, end);

      print('\n--- Batch $batchNum (${batch.length} records) ---');

      for (final order in batch) {
        final id = order['id'] as String?;
        final amount = order['amount'] as double?;
        final status = order['status'] as String?;

        // Pular registros inválidos
        if (id == null || amount == null || status == null) {
          skippedCount++;
          continue;
        }

        if (amount <= 0) {
          skippedCount++;
          continue;
        }

        final tier = classifyAmount(amount);

        if (status == 'completed') {
          processedCount++;
          totalRevenue += amount;
          print('  $id: \$${amount.toStringAsFixed(2)} USD [$tier]');
        } else if (status == 'pending') {
          print('  $id: PENDING - \$${amount.toStringAsFixed(2)} USD [$tier]');
        } else {
          skippedCount++;
        }
      }
    }
  }

  void printSummary() {
    print('\n=== Processing Summary ===');
    print('Processed: $processedCount orders');
    print('Skipped:   $skippedCount records');
    print('Revenue:   \$${totalRevenue.toStringAsFixed(2)} USD');
  }
}

void main() {
  final orders = <Map<String, dynamic>>[
    {'id': 'ORD-001', 'amount': 1500.0, 'status': 'completed'},
    {'id': 'ORD-002', 'amount': 15500.0, 'status': 'completed'},
    {'id': 'ORD-003', 'amount': -50.0, 'status': 'completed'},
    {'id': 'ORD-004', 'amount': 890.0, 'status': 'pending'},
    {'id': 'ORD-005', 'amount': 3200.0, 'status': 'completed'},
    {'id': 'ORD-006', 'amount': 0.0, 'status': 'cancelled'},
  ];

  final processor = BatchProcessor(batchSize: 3);
  processor.processBatch(orders);
  processor.printSummary();
}
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 dependendo da versão do SDK.

Saída:

TEXT
--- Batch 1 (3 records) ---
  ORD-001: $1500.00 USD [Premium]
  ORD-002: $15500.00 USD [Enterprise]
--- Batch 2 (3 records) ---
  ORD-004: PENDING - $890.00 USD [Standard]
  ORD-005: $3200.00 USD [Premium]

=== Processing Summary ===
Processed: 3 orders
Skipped:   3 records
Revenue:   $20200.00 USD

❓ Perguntas Frequentes

P: O break é obrigatório em instruções switch? R: Sim, na instrução switch do Dart, todo case não vazio deve terminar com break, return, throw ou continue. Não há fall-through. No entanto, um case vazio pode fazer fall-through.

P: O que é o _ em uma switch expression? R: _ é um padrão curinga que corresponde a todos os casos restantes, equivalente ao default em uma instrução switch. O Dart 3 recomenda usar _ em vez de default.

P: O for-in pode modificar uma coleção? R: Não. Modificar uma coleção (adicionar/remover elementos) durante uma travessia for-in causará um ConcurrentModificationError. Se modificação for necessária, colete as mudanças primeiro e aplique-as depois.

P: Quando devo usar while em vez de for? R: Use while quando o número de iterações é incerto (ex.: esperando uma resposta de rede). Use for/for-in quando o número de iterações é conhecido ou há uma coleção clara para percorrer.

P: Rótulos são comumente usados no desenvolvimento real? R: Não com frequência. break/continue com rótulo é usado principalmente para controle preciso em laços aninhados. Na maioria dos casos, aninhamento profundo pode ser evitado através de refatoração (extração de métodos, uso de funções de alta ordem).

P: A switch expression do Dart 3 suporta correspondência de múltiplos valores? R: Sim. Você pode usar | para combinar múltiplos padrões, como 'pending' | 'processing' => 'In Progress'.

P: Há diferença de performance entre do-while e while? R: Não há diferença de performance perceptível. A escolha depende da semântica: se o corpo do laço precisa executar pelo menos uma vez.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Use for-in para percorrer uma List contendo 5 valores de pedidos. Calcule o total e a média, pulando pedidos com valor menor ou igual a 0.
  2. Intermediário (Dificuldade ⭐⭐): Use uma switch expression para implementar um classificador de status de pedidos que retorne diferentes rótulos de prioridade de processamento com base na combinação de (status, valor).
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente um processador em lote paginado que processe N registros por lote, suporte interrupção com break (ao encontrar um erro grave) e salto com continue (para registros inválidos), e exiba um relatório final de estatísticas de processamento.

← 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%