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á
- if-else / switch-case (incluindo uma prévia das switch expressions do Dart 3)
- Laços for / while / do-while
- for-in e travessia de Iterable
- break / continue e controle com rótulos
- Cenário do Bob: Lógica de filtragem de dados no DataPipeline
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.
// 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',
};
> 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
- Switch expressions tornam a lógica de ramificação clara à primeira vista, eliminando pirâmides aninhadas.
- A travessia for-in é mais segura, evitando erros de índice fora dos limites.
- Controle com rótulos permite saltos precisos para break/continue em laços complexos.
3. Instruções Condicionais if-else
(1) Sintaxe Básica
▶ 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 dependendo da versão do SDK.
: Uso Básico de if-else
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');
}
}
> 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
> 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)
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');
}
> 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
> 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
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');
}
}
> 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
> 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
// 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
}
> 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
> 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
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');
}
}
> 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
> 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
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');
}
}
> 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
> 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
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);
}
> 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
> 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
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');
}
}
> 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
> 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
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');
}
}
}
> 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
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]
> 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
> 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
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');
}
}
> 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
// ============================================
// 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();
}
> 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:
--- 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
- if-else é a instrução condicional mais básica. O Dart 3 suporta sintaxe de if-expression.
- Switch expressions (Dart 3) são mais concisas e seguras que instruções switch, suportando verificações de exaustividade.
- Quatro tipos de laço têm diferentes casos de uso: for (iterações conhecidas), for-in (travessia de coleções), while (condicional), do-while (pelo menos uma vez).
- break sai de um laço, continue pula a iteração atual, e rótulos permitem saltos mais precisos em laços aninhados.
- O DataPipeline usa continue para filtrar dados inválidos e switch expressions para classificar níveis de pedidos.
📝 Exercícios
- 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.
- 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).
- 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.