Ficha 01 · Abordagens e widgets Flutter
- Comparar abordagens
- Construir UI com widgets
- Distinguir stateless/stateful
- Aplicar setState
Parte I · Abordagens
Exercício 1 · Escolher abordagem (15 pts)
Para cada caso, indica a abordagem (nativo / cross-platform / PWA) e justifica:
a) Startup com 2 programadores quer lançar app Android+iOS rápido.
b) Jogo 3D com gráficos intensivos.
c) Site de notícias quer "app" sem custo de loja.
d) App bancária com requisitos de segurança e biometria.
a) Cross-platform (Flutter/RN) — 1 código, 2 plataformas, equipa pequena, time-to-market.
b) Nativo — performance máxima, acesso total à GPU/APIs gráficas.
c) PWA — sem loja, instala do browser, 1 código web, sem custo de publicação.
d) Nativo (ou cross-platform com módulos nativos) — controlo fino de segurança, biometria, secure enclave. Muitas apps bancárias usam Flutter/RN com plugins nativos para a parte sensível.
Exercício 2 · Prós e contras (10 pts)
Completa:
| Abordagem | 1 vantagem | 1 desvantagem |
|---|---|---|
| Nativo | ||
| Flutter | ||
| PWA |
| Abordagem | Vantagem | Desvantagem |
|---|---|---|
| Nativo | Performance e integração máximas | 2 bases de código (Android+iOS) |
| Flutter | 1 código → várias plataformas | Algum overhead; casos extremos exigem código nativo |
| PWA | Sem loja, actualização instantânea | Acesso limitado a hardware; iOS restringe |
Parte II · Widgets
Exercício 3 · App mínima (15 pts)
Escreve uma app Flutter mínima com AppBar "Aulify" e texto centrado "Olá".
import 'package:flutter/material.dart';
void main() => runApp(const MinhaApp());
class MinhaApp extends StatelessWidget {
const MinhaApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Aulify')),
body: const Center(child: Text('Olá')),
),
);
}
}
Exercício 4 · Layout (20 pts)
Constrói um widget que mostra (em coluna): um título, uma imagem da rede e uma lista de 3 produtos com ícone.
class Loja extends StatelessWidget {
const Loja({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Loja')),
body: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text('Produtos em destaque',
style: TextStyle(fontSize: 22, fontWeight: FontWeight.bold)),
),
Image.network('https://picsum.photos/400/200'),
Expanded(
child: ListView(
children: const [
ListTile(leading: Icon(Icons.edit), title: Text('Caneta')),
ListTile(leading: Icon(Icons.book), title: Text('Caderno')),
ListTile(leading: Icon(Icons.backpack), title: Text('Mochila')),
],
),
),
],
),
);
}
}
Expanded faz a ListView ocupar o espaço restante e ter scroll.
Parte III · Estado
Exercício 5 · Stateless vs Stateful (10 pts)
Para cada widget, indica se deve ser Stateless ou Stateful:
a) Logótipo fixo. ___
b) Contador de cliques. ___
c) Cartão que mostra o nome de um produto recebido por parâmetro. ___
d) Checkbox que o utilizador liga/desliga. ___
e) Formulário com campos editáveis. ___
a) Stateless — não muda. b) Stateful — o número muda. c) Stateless — recebe dado e mostra, não muda sozinho. d) Stateful — o estado ligado/desligado muda. e) Stateful — guarda o que o utilizador escreve.
Regra: se o widget muda a si próprio ao longo do tempo → Stateful. Se só mostra o que recebe → Stateless.
Exercício 6 · Contador (20 pts)
Implementa um StatefulWidget "Contador" com um número, um botão +1 e um botão Reset.
class Contador extends StatefulWidget {
const Contador({super.key});
@override
State<Contador> createState() => _ContadorState();
}
class _ContadorState extends State<Contador> {
int _n = 0;
void _incrementar() => setState(() => _n++);
void _reset() => setState(() => _n = 0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Contador')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$_n', style: const TextStyle(fontSize: 48)),
const SizedBox(height: 16),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: _incrementar,
child: const Text('+1'),
),
const SizedBox(width: 12),
OutlinedButton(
onPressed: _reset,
child: const Text('Reset'),
),
],
),
],
),
),
);
}
}
setState() é essencial — sem ele, a variável muda mas a UI não actualiza.
Exercício 7 · Bug de estado (10 pts)
Este código não actualiza o ecrã quando se clica. Porquê e como corrigir?
int n = 0;
ElevatedButton(
onPressed: () { n++; },
child: Text('$n'),
)
Problema: n++ altera a variável mas não chama setState(), por isso o Flutter não sabe que tem de reconstruir o widget. O ecrã continua a mostrar o valor antigo.
Correcção (dentro de um StatefulWidget):
ElevatedButton(
onPressed: () => setState(() => n++),
child: Text('$n'),
)
setState() marca o widget como "sujo" e o Flutter reconstrói-o com o novo valor. (Além disso, n tem de estar na classe State, não solto.)