Flutter¶
Flutter é o framework do Google para criar aplicativos móveis (Android e iOS), web e desktop a partir de uma única base de código, escritos em Dart. A promessa é juntar o desempenho do desenvolvimento nativo com a produtividade de escrever um só aplicativo.
Sobre a versão
O livro-fonte (2020) usa Flutter 1.12 e Dart 2.7. Esta página usa o Flutter/Dart atuais; onde o código mudou há uma nota "Hoje". Trechos marcados como complemento não estão no livro. Para o desenvolvimento web equivalente em TypeScript veja Angular.
Por que o Flutter surgiu: o problema do mobile¶
O desenvolvimento móvel vivia em uma gangorra:
| Abordagem | Vantagem | Custo |
|---|---|---|
| Nativa (Kotlin/Java no Android; Swift no iOS) | Máximo desempenho e acesso ao hardware | Um aplicativo para cada plataforma, com retrabalho de equipe e custo |
| Híbrida (Cordova, Ionic: HTML/CSS/JS em uma web view) | Uma base de código, equipe web reaproveitada | Animações e inicialização mais lentas; comunicação com o hardware passa por camadas (JS → web view → código nativo) |
O Flutter tenta o "mundo ideal": um código, várias plataformas e desempenho próximo do nativo. Vantagens destacadas na fonte: hot reload (alterações aparecem em segundos, sem recompilar tudo e sem perder o estado), inicialização rápida, animações fluidas, UI consistente nas plataformas, acesso direto ao hardware por uma camada fina, ambiente de desenvolvimento simples e packages da comunidade.
Definição: Single code base (base de código única)
Escrever o aplicativo uma vez e gerá-lo para várias plataformas. Flutter, React Native e Ionic fazem isso; os três acessam recursos do dispositivo (câmera, GPS, acelerômetro, vibração) por plugins.
Complemento — como o Flutter desenha: ele não usa os componentes nativos de cada sistema; leva o próprio motor gráfico (Skia, hoje também Impeller) e desenha cada pixel da interface. Por isso a aparência é idêntica nas plataformas e as animações são suaves. A contrapartida é um aplicativo maior e, às vezes, a necessidade de adaptar o visual ao "jeito" de cada sistema.
O que é preciso para desenvolver¶
| Plataforma-alvo | Requisitos |
|---|---|
| iOS | Obrigatoriamente um Mac com Xcode; para executar em dispositivo real, um iPhone/iPad; conta de desenvolvedor Apple (paga, anual) só para publicar na App Store |
| Android | Windows, macOS ou Linux com Android Studio/SDK; um aparelho real é recomendado (o emulador nem sempre reproduz fielmente); conta de desenvolvedor Google (taxa única) só para publicar na Google Play |
Instale o SDK do Flutter e um editor (VS Code ou Android Studio com plugins Flutter/Dart); o comando
flutter doctor verifica o ambiente.
Estrutura de um projeto¶
| Item | Função |
|---|---|
lib/main.dart |
Ponto de partida: contém a função main() que chama runApp(...) |
android/ e ios/ |
Projetos nativos "hospedeiros" de cada plataforma, que embutem o aplicativo Flutter; só se mexe neles para configurações nativas (permissões, ícone, assinatura). Não é preciso saber Kotlin/Swift para o básico |
test/ |
Testes automatizados (widget_test.dart de exemplo) |
pubspec.yaml |
Coração do projeto: nome, versão do SDK, dependências, assets (imagens, fontes). Em YAML: a indentação importa |
pubspec.lock |
Versões exatas resolvidas das dependências (garante builds repetíveis); gerado, não se edita |
.gitignore, .metadata, README.md |
Controle de versão (não versionar artefatos de compilação), metadados do SDK (versionar, não editar) e documentação |
Dart: a linguagem do Flutter¶
Dart (Google) é orientada a objetos, de tipagem estática com sintaxe familiar a quem vem de Java, C# ou JavaScript. Por que foi escolhida:
- JIT e AOT: em desenvolvimento roda em JIT (Just-in-Time), que permite o hot reload; em produção é compilada AOT (Ahead-of-Time) para código nativo, com inicialização rápida e comportamento previsível. Poucas linguagens oferecem os dois modos (compare com JVM e JIT).
- Layout na própria linguagem: sem XML, HTML ou JSX separados — a interface é código Dart declarativo, o que facilita ferramentas e refatoração.
- Concorrência sem memória compartilhada: Dart roda em uma thread por isolate; os isolates
não compartilham memória e se comunicam por mensagens (parecido com web workers), evitando
condições de corrida e a maioria dos bloqueios. O dia a dia usa
async/await,Future(um valor futuro) eStream(vários valores ao longo do tempo). - Coleta de lixo otimizada para muitos objetos de vida curta (a UI reativa recria a árvore de widgets a cada quadro), com alocação barata.
- Dart também pode ser transpilado para JavaScript (web) e rodar em servidor.
Future<Map<String, dynamic>> buscar() async {
final resposta = await http.get(Uri.parse('https://api.exemplo.com/dados')); // Hoje: Uri.parse
return json.decode(resposta.body) as Map<String, dynamic>;
}
Complemento — null safety: desde o Dart 2.12 tipos não aceitam null a menos que declarados
com ? (String? nome), o que elimina uma classe inteira de erros. O código do livro (Key key,
String que pode ser nulo) precisa de ajustes (Key? key, required).
Widgets: tudo é widget¶
Definição: Widget
Elemento de interface descrito de forma declarativa e imutável: botão, texto, lista, margem,
alinhamento, o próprio aplicativo. A tela é uma árvore de widgets; alguns recebem um filho
(child) e outros uma lista (children). O Flutter segue o modelo reativo (inspirado no React):
quando o estado muda, os widgets são reconstruídos e o Flutter atualiza só o que mudou.
void main() => runApp(const MeuApp());
class MeuApp extends StatelessWidget {
const MeuApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp( // raiz com Material Design + navegação
title: 'Meu app',
home: Scaffold( // estrutura básica: appBar, body, FAB, rodapé...
appBar: AppBar(title: const Text('Exemplo')),
body: const Center(child: Text('Olá, Flutter!')),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
),
);
}
}
MaterialAppimplementa o Material Design (sistema visual do Google) e cria na raiz umNavigatorque gerencia as telas (rotas) como uma pilha: a última aberta é a primeira a fechar (Navigator.push/pop).Scaffoldé o esqueleto da tela (AppBar,body, botão flutuante, barra inferior). Há também o conjunto Cupertino (estilo iOS).- Existem widgets básicos (do SDK) e personalizados (seus ou de packages). Para evitar repetir
blocos (por exemplo, o mesmo
Paddingem volta de váriosText), extraia um widget próprio.
StatelessWidget x StatefulWidget¶
| StatelessWidget | StatefulWidget | |
|---|---|---|
| Estado | Nenhum: totalmente estático | Tem estado mutável |
| Uso | Estruturas fixas (telas, menus, textos) | Tudo que depende de interação, API ou dados que mudam (contador, formulário) |
| Implementação | Uma classe com build() |
Duas classes: o widget e o State, onde ficam as variáveis e o build() |
| Atualização | — | setState(() { ... }) avisa o Flutter que algo mudou e dispara nova construção |
class Contador extends StatefulWidget {
const Contador({super.key});
@override
State<Contador> createState() => _ContadorState();
}
class _ContadorState extends State<Contador> {
int _toques = 0; // estado
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => _toques++), // muda o estado e reconstrói
child: Text('Toques: $_toques'),
);
}
}
O estado fica no State, não na árvore de widgets (que só carrega a representação visual): cada widget
cuida do seu estado. Esquecer o setState é o erro clássico — a variável muda, mas a tela não.
Complemento: para estado compartilhado entre telas, setState não basta: usam-se gerenciadores como
Provider, Riverpod, Bloc ou GetX.
Dependências (packages)¶
Dependências são pacotes Dart/Flutter que acrescentam recursos (leitura de QR code, GPS, biometria, HTTP, banco de dados, animações). A comunidade os publica gratuitamente no repositório pub.dev.
# pubspec.yaml
dependencies:
flutter:
sdk: flutter
http: ^1.2.0 # ^ = qualquer versão compatível (mesma versão principal)
dev_dependencies: # só desenvolvimento: não vão para o app de produção
flutter_test:
sdk: flutter
flutter pub get # baixa as dependências (o editor costuma fazer ao salvar)
flutter pub add http # complemento: adiciona e instala
Para usar: import 'package:qrcode_reader/qrcode_reader.dart';. Avalie antes de adotar: manutenção
recente, popularidade, compatibilidade com null safety e com as plataformas-alvo. Plugins que acessam o
hardware exigem permissões nos projetos nativos.
Prototipação: planejar antes de programar¶
"Quem sabe para onde quer ir chega mais rápido." O desenvolvedor costuma ser bom em fazer funcionar, mas não necessariamente em design e usabilidade, e programar enquanto planeja ("fazejamento") produz um aplicativo "Frankenstein", remendado de layouts e retrabalho. Boas práticas:
- Prototipe antes de codar: wireframes no papel ou em ferramentas como Figma ou Adobe XD, inspirando-se em referências de UI; valide com o cliente (fluxos, telas, animações).
- Prefira interfaces simples ("poucos botões, não um painel de avião").
- Prototipar reduz estouro de prazo, orçamento e frustração do cliente. Relação com Requisitos: prototipação é técnica de elicitação e validação.
Consumindo APIs: HTTP e JSON¶
- HTTP é o protocolo de requisição-resposta cliente-servidor da web; os verbos mais comuns são GET (buscar; parâmetros na URL, nunca dados sensíveis) e POST (enviar dados no corpo) (Backend).
- Uma API é o "caminho comum" entre duas aplicações para trocar dados, independentemente da linguagem; padrões: REST, SOAP, GraphQL; formatos: JSON, XML.
- JSON é uma notação em texto, independente de linguagem: objetos entre
{}, pareschave: valorseparados por vírgula, que podem conter outros objetos e listas. O Dart converte comjson.decode/json.encode, e o JSON vira umMap/List.
Padrão do aplicativo que consome dados: monta-se uma interface "genérica" que espera os dados; faz-se
a requisição assíncrona; decodifica-se o JSON; preenchem-se os campos. Para exibir dados que ainda
serão buscados, o Flutter oferece o FutureBuilder, que reconstrói a tela conforme o estado do
Future:
FutureBuilder<Map<String, dynamic>>(
future: buscar(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return const Center(child: CircularProgressIndicator()); // carregando
default:
if (snapshot.hasError) return const Text('Erro ao carregar dados');
return Text('${snapshot.data}');
}
},
)
Em formulários, controladores (TextEditingController) permitem ler e alterar o valor dos campos. Chaves
de API não devem ficar no código versionado.
Banco de dados local: SQLite¶
Para persistir dados no dispositivo, o pacote sqflite traz o SQLite (banco relacional, o mais usado do mundo, de arquivo único) ao Flutter. É a mesma ideia de SQL e do padrão DAO de Acesso a dados.
- Dado x informação: guarde o dado (data de nascimento) e calcule a informação (idade) na hora de exibir — a idade armazenada ficaria obsoleta.
- Estrutura em camadas: pasta
providers/(acesso ao banco: criar tabela,insert,query,update,delete) epages/(telas). Os métodos são assíncronos (Future). - Singleton: uma única instância do provider (com
factorye instância estática) mantém uma só conexão com o banco; a fonte lembra que é considerado um antipadrão por criar acoplamento e estado global (veja a visão crítica em Padrões arquiteturais). - Use consultas parametrizadas (
where: 'id = ?', whereArgs: [id]), nunca concatenação (SQL injection). - O pacote
url_launcherabre o discador, o navegador ou o e-mail; ilustra como combinar dependências.
Testes automatizados em widgets¶
Por que testar de forma automatizada
Testes manuais são lentos, não repetíveis e tendem a repetir os mesmos caminhos; quem acompanhou o desenvolvimento "vicia" e não testa entradas inesperadas. Testes automatizados rodam dezenas de vezes por dia em segundos e protegem de regressões (como subir uma atualização na sexta-feira e passar o fim de semana consertando). TDD é escrever o teste junto (ou antes) da funcionalidade — Qualidade.
A biblioteca flutter_test já vem em dev_dependencies (e por isso não vai para o aplicativo final). Um
teste tem três partes: o que testar, a ação e o resultado esperado.
import 'package:flutter_test/flutter_test.dart';
void main() {
const valorSemDesconto = 150.0;
test('deve lançar erro com valor negativo', () {
expect(() => calcularDesconto(-10, 20), throwsArgumentError);
});
testWidgets('botão incrementa o contador', (tester) async { // teste de widget
await tester.pumpWidget(const MaterialApp(home: Contador()));
expect(find.text('Toques: 0'), findsOneWidget);
await tester.tap(find.byType(GestureDetector));
await tester.pump(); // reconstrói a tela
expect(find.text('Toques: 1'), findsOneWidget);
});
}
Rode com flutter test. Há três níveis: unitário (funções), de widget (a UI isolada) e de
integração (o app inteiro em dispositivo). Exemplo da fonte: função de desconto testada com valores
válidos, inválidos e limites.
Ícone do aplicativo¶
O ícone é o "chamariz" de um app fechado e carrega a marca. Em vez de gerar dezenas de tamanhos à mão,
use o pacote flutter_launcher_icons: um PNG quadrado e sem fundo (ideal 1024 × 1024), declarado em
pubspec.yaml, e o comando gera todos os tamanhos para Android e iOS:
dev_dependencies:
flutter_launcher_icons: ^0.13.1
flutter_launcher_icons:
image_path: "assets/icon.png"
android: true
ios: true
flutter pub get
dart run flutter_launcher_icons # (no livro: flutter pub run flutter_launcher_icons:main)
Evite nomear o projeto como palavras reservadas por bibliotecas (ex.: icon).
Flutter x Ionic x React Native: qual escolher?¶
Pensamento de "bala de prata" (uma solução simples para um problema complexo): nenhuma tecnologia serve para tudo — escolha pelo projeto, não pelo gosto, e valide logo no início os requisitos críticos (ex.: geolocalização em segundo plano, pagamento dentro do app). Descobrir meses depois que o pacote essencial não existe é o pior cenário.
| Ionic | React Native | Flutter | |
|---|---|---|---|
| Linguagem | HTML/CSS/JS (Angular, React ou Vue) | JavaScript/TypeScript com JSX | Dart |
| Como renderiza | Web view dentro de um app nativo | Converte para componentes nativos | Motor próprio desenha a UI |
| Desempenho | Menor (web view); fraco em tarefas em segundo plano | Bom | Muito bom (código nativo AOT) |
| Equipe ideal | Já domina web; formulários e protótipos | Domina JavaScript/React | Aceita aprender Dart; quer desempenho e UI consistente |
| Reuso para web | Fácil | Menos direto | Possível (Flutter Web) |
| Pontos de atenção | Plugins desatualizados, obsolescência | Curva do React, dependência de pontes nativas | Ecossistema/mão de obra menores (na época), pacotes ainda não portados |
Complemento: o ecossistema do Flutter cresceu muito desde 2020 e hoje é amplamente usado em produção; para decidir, considere também a equipe disponível, o tempo de entrega, a necessidade de integrações nativas profundas e o custo de manutenção (Plano de carreira para o lado do mercado). Uma alternativa a escrever só em Flutter é o desenvolvimento nativo quando o produto depende ao máximo de recursos específicos de cada plataforma.
Perguntas comuns de entrevista (resumo)¶
| Pergunta | Resposta curta |
|---|---|
| O que é Flutter? | Framework do Google que gera apps para várias plataformas a partir de uma base de código em Dart, desenhando a UI com motor próprio |
| Por que Dart? | Compila AOT para nativo (rápido) e roda em JIT no desenvolvimento (hot reload); sintaxe familiar; layout em código; isolates sem memória compartilhada |
| O que é hot reload? | Injetar o código alterado na aplicação em execução em segundos, preservando o estado |
| Stateless x Stateful? | Stateless não tem estado mutável; Stateful guarda estado em um State e usa setState para reconstruir |
O que faz o setState? |
Notifica o Flutter de que o estado mudou para reconstruir o widget |
| Como gerenciar estado entre telas? | Provider, Riverpod, Bloc, GetX etc. |
O que é um Future/FutureBuilder? |
Valor assíncrono futuro; o FutureBuilder constrói a UI conforme o estado do Future |
| Como persistir dados localmente? | SQLite via sqflite, shared_preferences para chaves simples, ou bancos como Hive/Isar |
| Flutter x React Native? | Flutter desenha a UI com motor próprio (Dart); React Native usa componentes nativos controlados por JavaScript |
| Como testar? | flutter_test: testes unitários, de widget (testWidgets, find, tap, pump) e de integração |