Dart: Fluxo de Controle no Dart
Última atualização: 2026-08-26
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 preview das switch expressions do Dart 3)
- Loops for / while / do-while
- for-in e travessia de Iterable
- break / continue e controle com labels
- Cenário do Bob: Lógica de filtragem de dados do DataPipeline
2. A História Real de um Desenvolvedor
(1) Dor: if-else Aninhados Tornam o Código Inmanutenível
Alice estava processando dados de pedidos de e-commerce e escreveu uma estrutura if-else de 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 alteração esquecida causou 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 usando 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 Pendente',
('pending', _) => 'Normal Pendente',
('shipped', _) => 'Em Trânsito',
('delivered', _) => 'Concluído',
_ => 'Desconhecido',
};
> **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 acesso fora dos limites.
- Controle com labels permite saltos precisos para break/continue em loops complexos.
3. Declaraçõ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 do if-else
void main() {
double orderAmount = 1500.0;
if (orderAmount > 1000) {
print('Pedido VIP - aplicar desconto');
} else if (orderAmount > 500) {
print('Pedido padrão');
} else {
print('Pedido pequeno');
}
}
> **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('Nível: $tier');
print('Rótulo: $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 |
|---|---|---|---|
| Declaraçã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) Declaraçã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.
: Declaração switch
void main() {
String status = 'shipped';
switch (status) {
case 'pending':
print('Pedido aguardando processamento');
break;
case 'shipped':
print('Pedido em trânsito');
break;
case 'delivered':
print('Pedido foi entregue');
break;
default:
print('Status desconhecido: $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' => 'Aguardando Processamento',
'shipped' => 'Em Trânsito',
'delivered' => 'Concluído',
'cancelled' => 'Cancelado',
_ => 'Status Desconhecido',
};
// Pattern matching com guards
String classifyOrder(double amount) => switch (amount) {
>= 10000 => 'Enterprise',
>= 1000 => 'Premium',
> 0 => 'Standard',
_ => 'Inválido',
};
void main() {
print(getStatusLabel('shipped')); // Em Trânsito
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 | Declaraçã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 Loop
(1) Loop 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.
: Loop for Tradicional
void main() {
// Loop for clássico
for (int i = 0; i < 5; i++) {
print('Processando lote ${i + 1}');
}
// Contagem regressiva
for (int i = 10; i > 0; i -= 2) {
print('Contagem regressiva: $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) Loop 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 com for-in
void main() {
List<String> sources = ['orders.csv', 'products.csv', 'customers.csv'];
// for-in com List
for (final source in sources) {
print('Carregando: $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 = {
'Eletrônicos': 500,
'Roupas': 300,
'Livros': 200,
};
for (final entry in categoryCount.entries) {
print('${entry.key}: ${entry.value} produtos');
}
}
> **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) Loops 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 - verificar condição primeiro
int retries = 0;
bool connected = false;
while (!connected && retries < 3) {
print('Tentativa de conexão ${retries + 1}...');
retries++;
connected = retries >= 2; // Sucesso simulado
}
// do-while - executar pelo menos uma vez
int batchSize = 10000;
int processed = 0;
do {
int chunk = (batchSize - processed > 1000) ? 1000 : batchSize - processed;
print('Processando $chunk registros...');
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 Loop | 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 | Percorrendo coleções |
while |
Antes da entrada | 0 | Dirigido por condição |
do-while |
Após execução | 1 | Deve executar pelo menos uma vez |
6. break, continue e Labels
(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('Válidos: $validCount, Total: $totalValid USD');
// break - parar no primeiro erro
for (final amount in amounts) {
if (amount < 0) {
print('Erro: Valor negativo $amount encontrado!');
break;
}
print('Processando: $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 Labels
void main() {
// Label para controle de loop aninhado
outer:
for (int batch = 0; batch < 3; batch++) {
for (int record = 0; record < 5; record++) {
if (record == 2 && batch == 1) {
print('Erro crítico no lote $batch, registro $record');
break outer; // Sair de ambos os loops
}
print('Lote $batch, Registro $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[for-in Iterar]
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 concluídos
if (status == 'completed') {
completedCount++;
totalRevenue += amount;
}
}
print('=== Relatório de Filtro do DataPipeline ===');
print('Pedidos concluídos: $completedCount');
print('Receita total: \$${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',
_ => 'Inválido',
};
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--- Lote $batchNum (${batch.length} registros) ---');
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: PENDENTE - \$${amount.toStringAsFixed(2)} USD [$tier]');
} else {
skippedCount++;
}
}
}
}
void printSummary() {
print('\n=== Resumo do Processamento ===');
print('Processados: $processedCount pedidos');
print('Ignorados: $skippedCount registros');
print('Receita: \$${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:
--- Lote 1 (3 registros) ---
ORD-001: $1500.00 USD [Premium]
ORD-002: $15500.00 USD [Enterprise]
--- Lote 2 (3 registros) ---
ORD-004: PENDENTE - $890.00 USD [Standard]
ORD-005: $3200.00 USD [Premium]
=== Resumo do Processamento ===
Processados: 3 pedidos
Ignorados: 3 registros
Receita: $20200.00 USD
❓ Perguntas Frequentes
P: O break é obrigatório em declarações switch? R: Sim, na declaração switch do Dart, cada 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 declaraçã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: Labels são comumente usados no desenvolvimento real? R: Não muito. break/continue com labels é usado principalmente para controle preciso em loops 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 ordem superior).
P: A switch expression do Dart 3 suporta matching de múltiplos valores? R: Sim. Você pode usar
|para combinar múltiplos padrões, como'pending' | 'processing' => 'Em Andamento'.
P: Há diferença de desempenho entre do-while e while? R: Não há diferença de desempenho perceptível. A escolha depende da semântica: se o corpo do loop precisa executar pelo menos uma vez.
📖 Resumo
- if-else é a declaração condicional mais básica. O Dart 3 suporta sintaxe de if-expression.
- Switch expressions (Dart 3) são mais concisas e seguras que declarações switch, suportando verificações de exaustividade.
- Quatro tipos de loop têm diferentes casos de uso: for (iterações conhecidas), for-in (percorrendo coleções), while (dirigido por condição), do-while (pelo menos uma vez).
- break sai de um loop, continue pula a iteração atual, e labels permitem saltos mais precisos em loops 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 retorna diferentes rótulos de prioridade de processamento com base na combinação de (status, valor).
- Desafio (Dificuldade ⭐⭐⭐): Implemente um processador em lote paginado que processa N registros por lote, suporta interrupção com break (ao encontrar erro severo) e salto com continue (para registros inválidos), e gera um relatório final de estatísticas de processamento.