PDM · DAW 2

Aula 03 · Semana 3 · Quarta, 23 de setembro de 2026 · 16:00 às 18:00

Dart I (continuação) e primeiro repositório no GitHub

Continuação da aula de ontem. Prática: os blocos 4 a 6 de Dart no DartPad (coleções, classes, controlo de fluxo) e o primeiro repositório no GitHub, com os exercícios guardados.

DartPad: dartpad.dev

No fim desta aula consegues

4. Coleções (1 de 3): List, Map e Set

void main() {
  final List<String> cidades = ['Ponta Delgada', 'Angra do Heroísmo', 'Horta'];
  final Map<String, double> temperaturas = {
    'Ponta Delgada': 21.0,
    'Angra do Heroísmo': 20.5,
    'Horta': 19.0,
  };
  final Set<String> ilhas = {'São Miguel', 'Terceira', 'Faial', 'Terceira'};

  cidades.add('Vila do Porto');
  print(cidades.length);                     // 4
  print(ilhas.length);                       // 3: um Set não guarda repetidos
  print(ilhas.contains('Pico'));             // false
  print(temperaturas['Horta']);              // 19.0; chave inexistente dá null

  for (final c in cidades) {
    print('$c → ${temperaturas[c] ?? 'sem dados'}');
  }
}

4. Coleções: map, where e reduce em três linhas só para ler

As três recebem uma função e aplicam-na a cada elemento. Chamam-se funções de ordem superior, e resolvem quase tudo o que se faz com coleções.

ChamadaResultadoO que faz
[🌽, 🐮, 🐔].map(cozinhar)[🍿, 🍔, 🍳]Transforma cada elemento. Sai com o mesmo tamanho.
[🍿, 🍔, 🍳].where(vegetariano)[🍿, 🍳]Fica só com os que passam no teste. Pode encolher.
[🍿, 🍔, 🍳].reduce(comer)💩Junta tudo num só valor.

Em Dart, o filter de outras linguagens chama-se where. As três existem em qualquer Iterable: listas, sets, e entries, keys ou values de um Map.

Adaptado do exemplo em JavaScript de Steven Luscher.

4. Coleções (2 de 3): where, map e reduce

// Cidades com 20 °C ou mais
final quentes = <String>[];
for (final e in temperaturas.entries) {
  if (e.value >= 20) quentes.add(e.key);
}

// Soma das temperaturas
var soma = 0.0;
for (final t in temperaturas.values) {
  soma += t;
}
  // ...continua dentro do main
  final quentes = temperaturas.entries
      .where((e) => e.value >= 20)           // fica com as entradas que passam no teste
      .map((e) => e.key)                     // de cada entrada, só a chave
      .toList();
  print(quentes);                            // [Ponta Delgada, Angra do Heroísmo]

  final soma = temperaturas.values.reduce((a, b) => a + b);
  print((soma / temperaturas.length).toStringAsFixed(1));   // 20.2
}

4. Coleções (3 de 3): spread, if e for dentro do literal

final tudo = <String>[];
tudo.addAll(cidades);
if (ilhas.length > 3) tudo.add('Lajes');
for (final i in ilhas) tudo.add('Ilha $i');
  // ...continua dentro do main
  final tudo = [
    ...cidades,                              // spread: todos os elementos de cidades
    if (ilhas.length > 3) 'Lajes',           // só entra se a condição for verdadeira
    for (final i in ilhas) 'Ilha $i',        // um elemento por ilha
  ];
  print(tudo.length);                        // 7
}

4. Coleções: o que interessa

Column(
  children: [
    const Text('Previsão'),
    if (chove) const Icon(Icons.umbrella),
    for (final c in cidades) Text(c),
  ],
)
Exercício 4 A partir do temperaturas do bloco, imprimir a cidade mais quente e a mais fria sem ordenar à mão (dica: reduce sobre temperaturas.entries, comparando value). Resultado esperado: Mais quente: Ponta Delgada (21.0) e Mais fria: Horta (19.0).

5. Classes (1 de 2)

enum TipoTempo { sol, nublado, chuva, trovoada }

class Previsao {
  final String cidade;
  final DateTime dia;
  final double tMin;
  final double tMax;
  final TipoTempo tipo;

  const Previsao({
    required this.cidade,
    required this.dia,
    required this.tMin,
    required this.tMax,
    this.tipo = TipoTempo.sol,
  });

  double get amplitude => tMax - tMin;
  bool get chove => tipo == TipoTempo.chuva || tipo == TipoTempo.trovoada;
  // continua no próximo slide
}

5. Classes (2 de 2)

  // ...dentro de Previsao
  Previsao copyWith({double? tMin, double? tMax, TipoTempo? tipo}) => Previsao(
        cidade: cidade,
        dia: dia,
        tMin: tMin ?? this.tMin,
        tMax: tMax ?? this.tMax,
        tipo: tipo ?? this.tipo,
      );

  @override
  String toString() => '$cidade ${dia.day}/${dia.month}: $tMin–$tMax °C, ${tipo.name}';
}

void main() {
  final hoje = Previsao(cidade: 'Ponta Delgada', dia: DateTime(2026, 9, 22), tMin: 17, tMax: 23);
  print(hoje);
  print(hoje.amplitude);
  final amanha = hoje.copyWith(tMax: 25, tipo: TipoTempo.chuva);
  print('$amanha, chove: ${amanha.chove}');
}

5. Classes: o que interessa

Exercício 5 No mesmo ficheiro, criar uma classe Localidade com nome, ilha e uma lista previsoes de Previsao. Adicionar um getter tMaxSemana com a maior tMax das previsões, e um método diasComChuva() com o número de previsões em que chove é verdadeiro. No main, com hoje e amanha já existentes: Localidade(nome: 'Ponta Delgada', ilha: 'São Miguel', previsoes: [hoje, amanha]) deve dar tMaxSemana igual a 25.0 e diasComChuva() igual a 1.

6. Controlo de fluxo e switch com padrões

String icone(TipoTempo t) => switch (t) {
      TipoTempo.sol => '☀️',
      TipoTempo.nublado => '☁️',
      TipoTempo.chuva => '🌧️',
      TipoTempo.trovoada => '⛈️',
    };

String aviso(double vento) {
  if (vento >= 90) return 'vermelho';
  if (vento >= 70) return 'laranja';
  if (vento >= 50) return 'amarelo';
  return 'verde';
}

void main() {
  for (final t in TipoTempo.values) {
    print('${t.name}: ${icone(t)}');
  }
  var i = 0;
  while (i < 3) { print(aviso(40.0 + i * 25)); i++; }
}

O switch como expressão é exaustivo: se um valor novo entrar no enum sem ser tratado, o compilador avisa. Em mobile isto evita ecrãs em branco quando a API devolve um estado novo.

Exercício 6 No mesmo ficheiro dos blocos 5 e 6: acrescentar nevoeiro ao TipoTempo e observar o erro do compilador no switch; corrigir com um ícone à escolha. Depois usar icone(tipo) no toString de Previsao, para print(amanha) mostrar 🌧️ Ponta Delgada 22/9: 17.0–25.0 °C, chuva.

Git e GitHub: primeiro repositório

Objetivo: os exercícios de hoje guardados num repositório próprio no GitHub, com commit e push feitos a partir do VS Code. Passos completos na secção Git do guia.

  1. No GitHub: New repository → nome pdm-dart → marcar Add a README file → Create.
  2. No VS Code: Ctrl+Shift+P → Git: Clone → colar o URL do repositório → escolher a pasta de trabalho (C:\dev) → abrir o projeto. Se aparecer Restricted Mode, carregar em Trust.
  3. Criar exercicio1.dart a exercicio6.dart com o código do DartPad (o 5 e o 6 podem ficar no mesmo ficheiro). Confirmar que correm: dart run exercicio1.dart.
  4. Vista Source Control (Ctrl+Shift+G): mensagem Exercícios de Dart das aulas 2 e 3 → Commit → Sync Changes.
  5. Na primeira vez, o browser abre para autorizar o acesso ao GitHub. Depois de autorizado, não volta a pedir password neste computador.
  6. Abrir o repositório no GitHub e confirmar que os ficheiros lá estão.

Git: o que interessa

Exercício 7 Fazer uma alteração a um exercício, novo commit com mensagem clara, push. Confirmar no GitHub que o histórico mostra dois commits.

Até sexta

Para fazer até sexta

  • Seis exercícios de Dart no repositório pdm-dart, com push feito.
  • Ler o Dart language tour até "Classes".
  • Setup: flutter doctor com ✓ em Flutter, Android toolchain e Android Studio; emulador criado (ou telemóvel com depuração USB). Confirma-se na sexta, na aula.

Para ler