Dart: Coleções no Dart

Última atualização: 2026-08-26

Coleções são o exército dos dados — listas ordenadas, conjuntos deduplicados, mapas indexados. Combine-os e você se torna invencível.

1. O que Você Aprenderá


2. A História Real de um Desenvolvedor

(1) A Dor: Agrupamento e Agregação Manual é Demorado e Propenso a Erros

Bob precisa agrupar 1.200.000 pedidos por categoria, calculando a receita total e a contagem de pedidos para cada um. Ele implementou manualmente a lógica de agrupamento com um loop for, escrevendo 50 linhas de código, mas encontrou problemas como sobrescrita de chaves duplicadas e ordenação instável. Processar 1.200.000 registros levou 8 segundos.

(2) A Solução: Operações com Coleções

As operações com coleções do Dart combinadas com funções de alta ordem reduzem o agrupamento e a agregação a apenas 10 linhas de código, com uma melhoria de 40% na velocidade.

DART
// Agrupar pedidos por categoria e agregar
final byCategory = <String, List<Order>>{};
for (final order in orders) {
  byCategory.putIfAbsent(order.category, () => []).add(order);
}

// Ou usar fold para agregação
final revenueByCategory = orders.fold<Map<String, double>>(
  {}, (acc, o) => acc..update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount));
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

(3) Os Benefícios


3. List

(1) Criação e Operações Básicas

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Criação de List

DART
void main() {
  // Criação literal
  List<int> counts = [1, 2, 3, 4, 5];
  var amounts = <double>[1500.0, 3200.0, 890.0];

  // Lista expansível
  var orders = <String>['ORD-001', 'ORD-002'];
  orders.add('ORD-003');

  // Lista de comprimento fixo
  var buffer = List<double>.filled(5, 0.0);
  buffer[0] = 100.0;

  // Gerar lista
  var batch = List.generate(10, (i) => 'Batch-${i + 1}');

  print(counts);      // [1, 2, 3, 4, 5]
  print(orders);      // [ORD-001, ORD-002, ORD-003]
  print(buffer);      // [100.0, 0.0, 0.0, 0.0, 0.0]
  print(batch);       // [Batch-1, ..., Batch-10]
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Percurso e Ordenação de List

DART
void main() {
  var amounts = [1500.0, 890.0, 3200.0, 50.0];

  // Ordenar in-place
  amounts.sort();
  print(amounts);  // [50.0, 890.0, 1500.0, 3200.0]

  // Ordenar de forma decrescente (criar nova lista)
  var descending = [...amounts]..sort((a, b) => b.compareTo(a));
  print(descending);  // [3200.0, 1500.0, 890.0, 50.0]

  // Acessar elementos
  print(amounts.first);  // 50.0
  print(amounts.last);   // 3200.0
  print(amounts[2]);     // 1500.0

  // Sublista
  print(amounts.sublist(1, 3));  // [890.0, 1500.0]
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Spread e Collection-if/for

DART
void main() {
  var base = ['ORD-001', 'ORD-002'];
  var extra = ['ORD-003', 'ORD-004'];

  // Operador spread
  var combined = [...base, ...extra];
  print(combined);  // [ORD-001, ORD-002, ORD-003, ORD-004]

  // Spread com consciência de null
  List<String>? maybeList;
  var safe = ['header', ...?maybeList, 'footer'];
  print(safe);  // [header, footer]

  // Collection-if
  bool includePremium = true;
  var tiers = [
    'Standard',
    if (includePremium) 'Premium',
    'Enterprise',
  ];
  print(tiers);  // [Standard, Premium, Enterprise]

  // Collection-for
  var batch = [
    for (int i = 1; i <= 3; i++) 'Batch-$i',
  ];
  print(batch);  // [Batch-1, Batch-2, Batch-3]
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
Operação Sintaxe Descrição
spread ...list Espalha os elementos
spread com consciência de null ...?list Espalha uma lista anulável
collection-if if (cond) expr Inclusão condicional
collection-for for (var x in list) expr Geração em loop

4. Set

(1) Criação e Operações de Conjunto

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Deduplicação e Operações com Set

DART
void main() {
  // Criar a partir de lista (remove duplicatas)
  var allOrders = ['ORD-001', 'ORD-002', 'ORD-001', 'ORD-003'];
  var uniqueOrders = <String>{...allOrders};
  print(uniqueOrders);  // {ORD-001, ORD-002, ORD-003}

  // Operações de conjunto
  var setA = {1, 2, 3, 4, 5};
  var setB = {4, 5, 6, 7, 8};

  print(setA.intersection(setB));  // {4, 5} - Interseção
  print(setA.union(setB));         // {1, 2, 3, 4, 5, 6, 7, 8} - União
  print(setA.difference(setB));    // {1, 2, 3} - Diferença

  // Teste de pertencimento
  print(setA.contains(3));    // true
  print(setA.containsAll({1, 2}));  // true
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Aplicação de Set em Cenários de Deduplicação

DART
void main() {
  // Deduplicar categorias a partir da lista de pedidos
  var orders = [
    {'id': 'ORD-001', 'category': 'Electronics'},
    {'id': 'ORD-002', 'category': 'Books'},
    {'id': 'ORD-003', 'category': 'Electronics'},
    {'id': 'ORD-004', 'category': 'Clothing'},
    {'id': 'ORD-005', 'category': 'Books'},
  ];

  var categories = orders.map((o) => o['category'] as String).toSet();
  print(categories);  // {Electronics, Books, Clothing}
  print('Unique categories: ${categories.length}');  // 3
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
Operação Método Resultado
Interseção a.intersection(b) Elementos comuns
União a.union(b) Todos os elementos
Diferença a.difference(b) Elementos em a que não estão em b
Contém a.contains(e) bool
Contém Todos a.containsAll(b) bool

5. Map

(1) Criação e Operações

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Criação de Map e Operações Básicas

DART
void main() {
  // Criação literal
  var config = <String, dynamic>{
    'appName': 'DataPipeline',
    'maxRecords': 1000000,
    'taxRate': 0.08,
    'verbose': true,
  };

  // Acessar valores
  print(config['appName']);       // DataPipeline
  print(config['missing']);       // null
  print(config['missing'] ?? 'N/A');  // N/A

  // Adicionar/atualizar entradas
  config['outputPath'] = '/tmp/reports';  // Adicionar
  config['taxRate'] = 0.10;               // Atualizar

  // Atualização segura
  config.update('maxRecords', (v) => v * 2, ifAbsent: () => 500000);

  // Iterar
  config.forEach((key, value) => print('  $key: $value'));
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Iteração e Agregação com Map

DART
void main() {
  var orders = [
    {'id': 'ORD-001', 'category': 'Electronics', 'amount': 1500.0},
    {'id': 'ORD-002', 'category': 'Books', 'amount': 50.0},
    {'id': 'ORD-003', 'category': 'Electronics', 'amount': 3200.0},
    {'id': 'ORD-004', 'category': 'Clothing', 'amount': 890.0},
    {'id': 'ORD-005', 'category': 'Books', 'amount': 120.0},
  ];

  // Agrupar por categoria
  final byCategory = <String, List<Map<String, dynamic>>>{};
  for (final order in orders) {
    final cat = order['category'] as String;
    byCategory.putIfAbsent(cat, () => []).add(order);
  }

  // Agregar receita por categoria
  final revenue = <String, double>{};
  for (final entry in byCategory.entries) {
    final total = entry.value.fold<double>(
      0, (sum, o) => sum + (o['amount'] as double));
    revenue[entry.key] = total;
  }

  // Imprimir resultados
  revenue.forEach((cat, total) =>
      print('$cat: \$${total.toStringAsFixed(2)} USD'));
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

6. Inferência de Tipos em Coleções e Coleções Const

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Inferência de Tipos e Coleções Const

DART
void main() {
  // Inferência de tipos
  var list1 = [1, 2, 3];           // List<int>
  var list2 = [1, 2, 3.0];         // List<num> (mistura de int e double)
  var list3 = <String>[];          // Genérico explícito para lista vazia

  // Coleções const - profundamente imutáveis
  const categories = ['Electronics', 'Books', 'Clothing'];
  const config = <String, int>{'batchSize': 10000, 'timeout': 30};
  const flags = {true, false};

  // categories.add('Sports');  // Erro! Não é possível modificar const

  // Const em classe
  const defaultFormats = ['json', 'csv', 'html'];
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
Recurso var final const
Conteúdo Mutável Sim Sim (referência imutável) Não
Inferência de Tipo Automática Automática Automática
Constante em Tempo de Compilação Não Não Sim

7. Cenário do Bob: Agrupamento e Agregação de Milhões de Pedidos

100%
flowchart LR
  A[List: 1.2M Pedidos] --> B[where: Filtrar]
  B --> C[groupBy: Categoria]
  C --> D[Map: Categoria → Pedidos]
  D --> E[fold: Agregar]
  E --> F[Saída: Estatísticas]
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

▶ Exemplo

TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

: Agrupamento e Agregação na Prática

DART
class Order {
  final String id;
  final String category;
  final double amount;
  final String status;

  Order({required this.id, required this.category, required this.amount, required this.status});
}

Map<String, Map<String, dynamic>> analyzeOrders(List<Order> orders) {
  // Filtrar pedidos concluídos
  final completed = orders.where((o) => o.status == 'completed').toList();

  // Agrupar por categoria
  final grouped = <String, List<Order>>{};
  for (final order in completed) {
    grouped.putIfAbsent(order.category, () => []).add(order);
  }

  // Agregar por categoria
  final result = <String, Map<String, dynamic>>{};
  for (final entry in grouped.entries) {
    final total = entry.value.fold<double>(0, (s, o) => s + o.amount);
    final avg = total / entry.value.length;
    result[entry.key] = {
      'count': entry.value.length,
      'total': total,
      'average': avg,
    };
  }
  return result;
}

void main() {
  final orders = [
    Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed'),
    Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed'),
    Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending'),
    Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed'),
    Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed'),
  ];

  final analysis = analyzeOrders(orders);
  print('=== Análise por Categoria ===');
  for (final entry in analysis.entries) {
    final stats = entry.value;
    print('${entry.key}:');
    print('  Pedidos:  ${stats['count']}');
    print('  Total:    \$${(stats['total'] as double).toStringAsFixed(2)} USD');
    print('  Média:    \$${(stats['average'] as double).toStringAsFixed(2)} USD');
  }
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

8. Exemplo Completo: Analisador de Coleções do DataPipeline

DART
// ============================================
// Analisador de Coleções do DataPipeline
// Demonstração completa de operações com List, Set, Map
// ============================================

class Order {
  final String id;
  final String category;
  final double amount;
  final String status;
  final String region;

  const Order({
    required this.id,
    required this.category,
    required this.amount,
    required this.status,
    this.region = 'US',
  });
}

class OrderAnalyzer {
  final List<Order> orders;

  OrderAnalyzer(this.orders);

  // Categorias únicas (Set)
  Set<String> get categories => orders.map((o) => o.category).toSet();

  // Regiões únicas (Set)
  Set<String> get regions => orders.map((o) => o.region).toSet();

  // Pedidos concluídos (filtro List)
  List<Order> get completed =>
      orders.where((o) => o.status == 'completed').toList();

  // Receita total (fold)
  double get totalRevenue =>
      completed.fold(0.0, (sum, o) => sum + o.amount);

  // Receita por categoria (agrupamento Map)
  Map<String, double> get revenueByCategory {
    return completed.fold<Map<String, double>>({}, (acc, o) {
      acc.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
      return acc;
    });
  }

  // Contagem de pedidos por região e status
  Map<String, Map<String, int>> get countByRegionStatus {
    final result = <String, Map<String, int>>{};
    for (final order in orders) {
      result.putIfAbsent(order.region, () => {});
      result[order.region]!.update(
        order.status, (v) => v + 1, ifAbsent: () => 1);
    }
    return result;
  }

  // Top N categorias por receita
  List<MapEntry<String, double>> topCategories(int n) {
    final sorted = revenueByCategory.entries.toList()
      ..sort((a, b) => b.value.compareTo(a.value));
    return sorted.take(n).toList();
  }

  void printReport() {
    print('=== Relatório de Analytics do DataPipeline ===');
    print('Total de pedidos:     ${orders.length}');
    print('Concluídos:          ${completed.length}');
    print('Categorias únicas:   ${categories.length}');
    print('Regiões:             ${regions}');
    print('Receita total:       \$${totalRevenue.toStringAsFixed(2)} USD');

    print('\n--- Receita por Categoria ---');
    for (final entry in revenueByCategory.entries) {
      print('  ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
    }

    print('\n--- Top Categorias ---');
    for (final entry in topCategories(3)) {
      print('  ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
    }

    print('\n--- Pedidos por Região e Status ---');
    for (final region in countByRegionStatus.entries) {
      print('  ${region.key}: ${region.value}');
    }
  }
}

void main() {
  final orders = [
    Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed', region: 'US'),
    Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed', region: 'EU'),
    Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending', region: 'US'),
    Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed', region: 'EU'),
    Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed', region: 'US'),
    Order(id: 'ORD-006', category: 'Electronics', amount: 2100.0, status: 'completed', region: 'AP'),
  ];

  OrderAnalyzer(orders).printReport();
}
TEXT 📖 Somente leitura
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.

Saída:

TEXT 📖 Somente leitura
=== Relatório de Analytics do DataPipeline ===
Total de pedidos:     6
Concluídos:          5
Categorias únicas:   3
Regiões:             {US, EU, AP}
Receita total:       $4660.00 USD

--- Receita por Categoria ---
  Electronics: $3600.00 USD
  Clothing: $890.00 USD
  Books: $170.00 USD

--- Top Categorias ---
  Electronics: $3600.00 USD
  Clothing: $890.00 USD
  Books: $170.00 USD

--- Pedidos por Região e Status ---
  US: {completed: 2, pending: 1}
  EU: {completed: 2}
  AP: {completed: 1}

❓ Perguntas Frequentes

P: Qual é a diferença de desempenho entre List e Set? R: List armazena elementos na ordem de inserção, com acesso por índice O(1) e busca O(n). Set é baseado em hash, oferecendo busca/verificação de pertencimento O(1), mas sem acesso por índice. Use Set para cenários de deduplicação e List quando a ordem importa.

P: As chaves de um Map podem ser de qualquer tipo? R: Sim, mas ao usar classes personalizadas como chaves, você deve sobrescrever == e hashCode; caso contrário, o Map não conseguirá encontrar as entradas corretamente. É recomendado usar String ou int como chaves.

P: Devo usar [] ou <Type>[] para uma List vazia? R: Use [] quando o contexto de inferência de tipo estiver disponível. Use <Type>[] ao declarar independentemente para evitar a inferência como List<dynamic>.

P: Collection-if e collection-for só podem ser usados em literais de List? R: Não, literais de List, Set e Map todos suportam collection-if e collection-for. Por exemplo: {for (var i in items) i.name: i.value}.

P: Qual é a diferença entre putIfAbsent e update do Map? R: putIfAbsent só define o valor se a chave não existir. update só atualiza o valor se a chave existir (use o parâmetro ifAbsent para lidar com casos de chave inexistente).

P: Qual é a diferença entre coleções const e coleções final? R: Uma coleção const é uma constante em tempo de compilação, com conteúdo inteiramente imutável (imutabilidade profunda). Uma coleção final tem uma referência imutável, mas o conteúdo pode ser mutável (ex: uma List final pode ter elementos adicionados).

P: Como posso ordenar uma List grande de forma eficiente? R: List.sort() ordena in-place, o que é mais eficiente do que [...list].sort() (que cria uma cópia). Se você não quiser modificar a lista original, crie uma cópia primeiro com [...list] e depois ordene.


📖 Resumo


📝 Exercícios

  1. Básico (Dificuldade ⭐): Crie uma List contendo 10 valores de pedidos. Use where para filtrar valores maiores que 500, map para formatá-los em USD, e reduce para calcular a soma total.
  2. Intermediário (Dificuldade ⭐⭐): Dado um conjunto de dados de pedidos, use putIfAbsent e fold do Map para agrupar pedidos por categoria, calculando a contagem de pedidos e a receita total para cada categoria, e gere um relatório formatado.
  3. Desafio (Dificuldade ⭐⭐⭐): Implemente uma classe CollectionPipeline que suporte encadeamento de operações como where, map, sortBy, groupBy e aggregate, gerando os resultados da análise. Dica: Cada etapa retorna uma nova coleção ou Map.

← Lição Anterior | Próxima Lição →

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%