Aula 03 · Semana 3 · Quarta, 23 de setembro de 2026 · 16:00 às 18:00
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.
where, map e reduce.final e toString.switch exaustivo com padrões.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'}');
}
}
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.
| Chamada | Resultado | O 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.
// 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
}
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
}
List, Map e Set são genéricos: List<String>, Map<String, double>. Com final cidades = [...] o tipo é inferido e é o mesmo. Escreve-se quando a coleção começa vazia: <String>[], <String, double>{}, <String>{}. Um {} vazio sem tipo é um Map.where, map, reduce, fold, any, every, firstWhere vivem em qualquer Iterable. Devolvem iteráveis preguiçosos; toList() materializa. Tudo o que fazem também se escreve com um ciclo: são atalhos, não magia...., if e for dentro de literais de coleção. Em Flutter, isto serve para construir listas de widgets condicionalmente.Column(
children: [
const Text('Previsão'),
if (chove) const Icon(Icons.umbrella),
for (final c in cidades) Text(c),
],
)
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).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
}
// ...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}');
}
final + construtor const com this.campo: a forma idiomática de modelo de dados imutável. É o que será usado para os dados da API.get define propriedades calculadas. copyWith é o padrão para "uma cópia com uma alteração", central em gestão de estado.enum com .name. Amanhã: enums com valores e métodos.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.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.
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.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.
pdm-dart → marcar Add a README file → Create.C:\dev) → abrir o projeto. Se aparecer Restricted Mode, carregar em Trust.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.Exercícios de Dart das aulas 2 e 3 → Commit → Sync Changes.git clone. Mais tarde, o projeto de grupo vive inteiro no GitHub, com branches e pull requests.pdm-dart, com push feito.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.