PDM · DAW 2

Aula 02 · Semana 3 · Terça, 22 de setembro de 2026 · 08:30 às 10:30

Como o Flutter funciona, e Dart I

Arranque: continuação do setup, com o emulador a instalar-se em segundo plano. Teoria: o que acontece entre o código e os pixels do ecrã, e porque o hot reload é possível. Prática: Dart no DartPad, sem instalação, e o primeiro repositório no GitHub.

Continuar o setup: o que fica a correr durante a teoria

Ontem ficou a instalação base. Hoje começa o que demora: o emulador. Quem tem telemóvel Android com cabo pode saltar este passo.

  1. Android Studio → ecrã inicial → menu de três pontos → Virtual Device Manager → +.
  2. Medium Phone → Next → aceitar a imagem de sistema sugerida → Finish. Descarrega cerca de 1 GB: fica a correr durante a teoria.
  3. No Windows, se pedir para ativar a Plataforma de Hipervisor do Windows: aceitar; obriga a reiniciar, por isso fazer no fim da aula ou em casa.
Device Manager do Android Studio com o botão + para adicionar um dispositivo virtual
Device Manager: o + cria o emulador; o triângulo arranca-o.
Janela Add Device com a lista de telefones, Medium Phone e Pixel
Add Device: Medium Phone é o mais leve; qualquer Pixel serve.

Ponto de situação

O que faltar fica para casa, com o guia; a prática de hoje corre no browser e não depende de nada disto.

Três camadas

Framework · Dart Material e Cupertino · widgets · layout · animações · gestos · tema. Código aberto dentro do SDK. Engine · C++ Impeller desenha · Dart VM (JIT em debug) ou código AOT (release) · texto e imagens · canais para a plataforma Embedder · Kotlin/Java, Swift/Objective-C, C++ Cria a Activity ou a janela · entrega toques e ciclo de vida · acessibilidade · platform channels Sistema operativo: uma superfície para desenhar e eventos de toque a app vive aqui
A app corre no framework. O sistema só entrega uma superfície e os toques; o Flutter faz o resto.
CamadaEscrita emO que faz
FrameworkDartO que se usa no dia a dia: widgets (Material, Cupertino), layout, animações, gestos, tema. Código Dart aberto, dentro do SDK.
EngineC++Desenha (Impeller), executa o Dart (Dart VM em desenvolvimento, código AOT em release), trata de texto, imagens, e fala com a plataforma.
EmbedderKotlin/Java, Swift/Objective-C, C++Específico de cada sistema: cria a janela ou a Activity, entrega eventos de toque, ciclo de vida, acessibilidade, e canais para código nativo.

O sistema dá uma superfície e os toques. O resto é o Flutter.

superfície em branco Sistema operativo Empresta um retângulo e entrega os toques. Flutter Pinta cada pixel: igual num Samsung de 2019 e num iPhone de 2026. câmara · GPS · notificações Hardware e serviços Só por uma ponte (platform channel), que os plugins do pub.dev escondem. Um mapa ou um vídeo nativos entram pela mesma ponte, como platform view.
O sistema não sabe o que a app desenha; só vê o retângulo pintado.

Tudo é um widget

Um widget é um objeto Dart imutável que descreve um pedaço de interface. Compõem-se uns dentro dos outros numa árvore.

import 'package:flutter/material.dart';

void main() => runApp(const TempoApp());

class TempoApp extends StatelessWidget {
  const TempoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('Tempo Açores')),
        body: const Center(child: Text('Ponta Delgada · 21 °C')),
      ),
    );
  }
}
Árvore de widgets do código acima MaterialApp Scaffold AppBar Center Text('Tempo Açores') Text('Ponta Delgada · 21 °C') Três árvores em paralelo Widgets descrição imutável do que deve aparecer; barata de recriar cria Elements instância viva na árvore; sobrevive a rebuilds cria RenderObjects layout e pintura; sabem tamanho e posição
Cada widget do código gera um element e, se ocupa espaço, um render object; as três árvores andam a par.
Analogia O widget é a planta de um edifício: imutável e barata de redesenhar. O element é a obra em curso, que segue a planta e sabe onde está na árvore. O render object é o edifício construído, com dimensões e posição. Mudar a planta não deita o edifício abaixo: a obra compara a planta nova com a antiga e só mexe no que mudou.

build não desenha nada

Devolve a descrição do que deve existir. O framework compara a descrição nova com a anterior e só mexe no que mudou. Chamar build muitas vezes é normal e barato; é assim que o Flutter foi desenhado.

Três árvores, uma ideia
  • Widgets são a descrição: baratos, imutáveis, recriados a cada build.
  • Elements são a instância viva que liga uma descrição a uma posição na árvore.
  • RenderObjects fazem layout e pintura.
O programador escreve widgets; o framework gere as outras duas.

Para os testes: um widget é imutável; o que muda é o estado, e o estado vive noutro sítio.

Analogia build é uma lista de compras, não a ida às compras. Escrever a lista muitas vezes é barato; o framework é quem vai à loja, e só compra o que mudou em relação à lista anterior.

Hot reload, hot restart, rebuild

Hot reload tecla r código alterado guardas o ficheiro Dart VM no telemóvel injeta código, chama build() estado mantido menos de 1 segundo Hot restart tecla R main() ou estado global ou o tipo de um estado reinicia a app do zero compilação mantida estado perdido alguns segundos Rebuild flutter run pubspec.yaml ou nativo dependências, recursos compila tudo de novo instala a app outra vez arranque a frio dezenas de segundos
Quanto mais em baixo, mais lento e mais se perde: hot reload por defeito; subir de nível só quando não chega.
Analogia Hot reload é trocar a receita a meio de cozinhar sem deitar fora o que já está na panela: o código muda, o estado fica. Hot restart deita fora a panela e começa de novo, mas com a cozinha já montada (a compilação). O rebuild completo é montar a cozinha outra vez.

Porquê Dart

Código Dart o mesmo ficheiro Em desenvolvimento: JIT corre numa máquina virtual no telemóvel Em release: AOT compilado para código máquina ARM Hot reload alterações em menos de 1 s App rápida sem interpretador a correr
Poucas linguagens fazem as duas coisas bem: rapidez a desenvolver e rapidez a correr.

Dart I no DartPad

Abrir dartpad.dev. Hoje não é preciso o Flutter: só Dart, no browser.

Constrói-se, passo a passo, o modelo de dados que a app do projeto vai usar. Seis blocos, cada um com um exercício curto no fim.

Escrever o código em vez de o copiar Escrever pratica a sintaxe; copiar não. Os erros do compilador fazem parte do exercício: convém lê-los. Os blocos marcados «só para ler» não se escrevem: servem para comparar.

1. Variáveis e tipos

void main() {
  var cidade = 'Ponta Delgada';   // tipo inferido: String
  int dia = 21;
  double temperatura = 21.5;
  bool chove = false;
  final ilha = 'São Miguel';      // atribuído uma vez, em runtime
  const maxDias = 5;              // constante em tempo de compilação

  print('$cidade, $ilha: $temperatura °C, chuva: $chove');
  print('Dia ${dia + 1} de $maxDias');
}

1. Variáveis e tipos: o que interessa

Exercício 1 No mesmo main, acrescentar double tMin = 17.0; e double tMax = 23.0;, calcular a amplitude (tMax - tMin) e imprimir, com interpolação e uma casa decimal (toStringAsFixed(1)), exatamente: Amplitude: 6.0 °C.

2. Null safety

void main() {
  String nome = 'Horta';
  String? alcunha;            // pode ser null; tem de ser declarado com ?

  // nome = null;             // erro de compilação
  print(alcunha ?? 'sem alcunha');        // ?? dá um valor por defeito
  print(alcunha?.length);                 // ?. só chama se não for null → null
  alcunha = 'Cidade do Mar';
  print(alcunha.length);                  // o compilador já sabe que não é null aqui

  late String descricao;      // prometo inicializar antes de usar
  descricao = '$nome, $alcunha';
  print(descricao);
}

2. Null safety: o que interessa

Exercício 2 No mesmo main, acrescentar int? humidade; e uma linha que imprime Humidade: n/d quando é null e Humidade: 80% quando tem valor, sem usar ! (dica: humidade == null ? 'n/d' : '$humidade%', ou ??). Testar as duas situações: uma vez antes e outra depois de humidade = 80;.

3. Funções

double celsiusParaFahrenheit(double c) => c * 9 / 5 + 32;

String descreve(String cidade, {required double tMax, double? tMin, bool chuva = false}) {
  final min = tMin == null ? '' : ', mín $tMin';
  return '$cidade: máx $tMax$min${chuva ? ', chuva' : ''}';
}

void main() {
  print(celsiusParaFahrenheit(21));
  print(descreve('Angra', tMax: 24));
  print(descreve('Horta', tMax: 22, tMin: 17, chuva: true));

  final formatar = (double t) => '${t.round()} °C';   // função como valor
  print(formatar(21.6));
}

3. Funções: o que interessa

Exercício 3 Escrever String classifica({required double t}) que devolve 'frio' abaixo de 15, 'ameno' até 25 e 'quente' acima. Depois usá-la dentro de descreve, para descreve('Horta', tMax: 22, tMin: 17, chuva: true) passar a devolver Horta: máx 22.0, mín 17.0, chuva (ameno).

Até amanhã

Para fazer até amanhã

  • Exercícios 1 a 3 guardados (no DartPad ou num ficheiro): amanhã continuam a partir deles.
  • Ler o Dart language tour até "Functions".
  • Setup: flutter doctor com ✓ em Flutter, Android toolchain e Android Studio; emulador criado (ou telemóvel com depuração USB). Confirma-se amanhã na aula.

Para ler