Partilhar: WhatsApp
aulify
UC UC02835 · T. Desenv. Software

Ficha 01 · Abordagens e widgets Flutter

Nativo vs híbrido, widgets, estado
Versão · Aluno
Tempo · 60 minutos
Cotação · 100 pontos
Aluno(a)
Turma
Data
Objectivos da ficha

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.)