Skip to content

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) e Stream (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),
        ),
      ),
    );
  }
}
  • MaterialApp implementa o Material Design (sistema visual do Google) e cria na raiz um Navigator que 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 Padding em volta de vários Text), 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 {}, pares chave: valor separados por vírgula, que podem conter outros objetos e listas. O Dart converte com json.decode / json.encode, e o JSON vira um Map/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) e pages/ (telas). Os métodos são assíncronos (Future).
  • Singleton: uma única instância do provider (com factory e 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_launcher abre 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