Jornada APIs e Integração de Sistemas
pessoa = {
"nome" : "Cácio Costa",
"empresa": "Alura",
"cargo" : "Instrutor e pagodeiro nas horas vagas"
}Jornada
-
Introdução às APIs
-
Fundamentos do REST
-
Design de APIs RESTful
-
Autenticação e Segurança de APIs REST
-
GraphQL
-
gRPC
-
API Gateways e BFF
-
Resiliência em APIs
-
Enterprise Integration Patterns
-
Event-driven Architecture

Seria legal se você soubesse...
-
... Desenvolvimento Web;
-
... Spring Boot e o ecossistema do Spring (módulos populares);
-
... SGBDs e Docker.

Aula 1
Introdução às APIs
1
Estilos de integração
2
Tecnologias de integração
3
CORBA, API e suas lições
5
Desafio
4
Arquiteturas de integração
Integração de sistemas
Estilos de integração






Estilos de integração
Considerações
- Vamos trocar funcionalidades ou apenas dados?
- Quais dados trocaremos?
- Qual protocolo utilizaremos?
- A comunicação será síncrona ou assíncrona?
- Quais ferramentas/frameworks utilizaremos?
- etc.

Evolução das tecnologias
Linha do tempo
Marcos importantes (1960 - 2024)
1960s
RPC
1980s
Mensageria
1990s
Corba e DCOM
2010s
REST
2000s
WebServices SOAP
2020s
GraphQL, gRPC
e Event-Driven
CORBA
A primeira grande tentativa de interoperabilidade universal

-
Desenvolvimento de sistemas distribuídos OO
-
Objetos remotos ou locais
-
-
Estabelecido pela OMG (Object Management Group) em 1991
-
Independência de linguagem
CORBA - Caiu atirando...
Grandes lições
-
A importância de contratos bem definidos entre sistema
-
Os desafios de criar abstrações universais
-
O equilíbrio entre poder e complexidade em APIs
module BankSystem {
interface Account {
void deposit(in float amount);
void withdraw(in float amount);
float balance();
};
};
Exemplo de IDL
Contratos são importantes!
A base de uma API
Endpoints/operações
O que um cliente é capaz de invocar no sistema remoto
Estruturas de dados
Formato das estruturas trafegadas nas requisições e respostas
Protocolo
O conjunto de regras que rege a comunicação para que os sistemas se entendam.
O que é uma API?
Elementos fundamentais
Documentação
Detalhamento de como um cliente deve utilizar a API.
Arquiteturas de integração
ESB centralizado
"One BUS to rule them all!"

Microsserviços
Defina "micro"!

Como era antes do Flutter?

-
Plataformas com arquiteturas próprias
-
O aplicativo tem que ser compilado para a plataforma específica
-
Duas SDKs (Xcode e Android SDK)
-
Bases de códigos distintas, com linguagens distintas
Por que usar Flutter?

-
A mesma base de código gera aplicativos para ambas plataformas
-
Curva de aprendizado menor
-
Mesma equipe de desenvolvimento
-
Novas plataformas (web e desktop)
-
SDKs de cada plataforma ainda é necessário
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.amber),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}O que preciso saber?
Dart
-
Tipos, variáveis e funções
-
Orientação a objetos
-
Estruturas de dados (listas, maps, ...)
-
Assincronismo (Future)
-
Programação funcional*
Ferramentas necessárias
Intellij IDEA + plugins
do Flutter e Dart
VS Code + Flutter extension
Android Studio + Flutter Plugin
OU
OU
IDE
E
Dart
Flutter
Linguagem e framework
Xcode (iOS)
Android SDK
E
OU
SDKs das plataformas
Virtual
+
Ambiente de desenvolvimento
IDE
- importar o projeto
- executar
3.
Flutter
- adicionar no PATH
- flutter doctor
- flutter create
1.
SDK da plataforma
- Xcode
- iniciar emulador
2.
Mão na massa...
Estrutura do projeto

Diretórios e arquivos:
-
lib: fica o código fonte do projeto
-
test: testes automatizados
-
build: onde são geradas as construções
-
android, ios, macos, web, windows: arquivos específicos de cada plataforma
-
pubspec.yaml: arquivo de configuração do projeto (definir dependências, assets e outras configurações
-
main.dart: arquivo de entrada do aplicativo
Qual a árvore mais famosa da programação?
A árvore do HTML! 😜
A lógica do Flutter

Assim como um documento HTML é organizado numa árvore de tags, um aplicativo em Flutter é organizado como uma árvore de widgets!"
Árvore de widgets
- Aplicativo tem um widget raiz como ponto de entrada
- Alguns widgets suportam só um filho
TUDO É WIDGET!
Biblioteca de widgets: https://docs.flutter.dev/ui/widgets

void main() {
runApp(
Text('Hello, World!', textDirection: TextDirection.ltr),
);
}
Widgets
-
Blocos de construção (você constrói a interface com eles)
-
Descrevem como a UI deve ser de acordo com sua configuração e estado
-
Inspirados no React
-
Visual e comportamento é um único lugar
void main() {
runApp(
Center(
child: Text('Hello, World!', textDirection: TextDirection.ltr),
),
);
}
Widgets com único filho
Center e Container
void main() {
runApp(
Center(
child: Container(color: Colors.purple, height: 200, width: 200),
),
);
}
Column(
children: [
Container(color: Colors.purple, height: 100, width: 100),
Container(color: Colors.deepPurpleAccent, height: 100, width: 100),
],
);
Widgets com vários filhos
Column e Row
Row(
children: [
Container(color: Colors.purple, height: 100, width: 100),
Container(color: Colors.deepPurpleAccent, height: 100, width: 100),
],
);
Mão na massa...
Não precisa decorar (tudo)
Biblioteca de widgets: https://docs.flutter.dev/ui/widgets
-
Escolha alguns sistemas que se integram dentro da Vivo;
-
Desenhe o fluxo de comunicação entre esses sistemas (ESB, ponto a ponto)
-
Faça uma engenharia reversa e documente parte dessa API
-
Endpoints
-
Operações
-
Estruturas de dados
-
Protocolo
-
Desafio
Nossas aulas
Próxima
Aula 1
11/12 às 11h
→ Introdução às APIs
Atual
Aula 2
11/12 às 15h
→ Fundamentos do REST
Aula 2
Fundamentos do REST
1
Fundamentos do REST
2
Mergulhar fundo no HTTP
3
Richardson Maturity Model
5
Desafio
4
Mão na massa
Fundamentos REST
Origem
"REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components."
Roy Fielding (2000)

Fundamentos REST
Restrições e princípios
| Restrição | Descrição |
|---|---|
| Cliente-Servidor | A UI é separada do armazenamento dos dados. |
| Stateless | Sem estado entre requisições. |
| Cache | Rquisições podem ser classificados como cacheáveis. |
| Interface Uniforme | Recursos, métodos, cabeçalhos, etc, padronizados. |
| Sistema em Camadas | Componentes só conhecem a camada imediatamente inferior |
| Código sob Demanda | Cliente pode estender funcionalidades com scripts (opcional) |
Roy chama esse estilo arquitetural da Web de Representational State Transfer, ou simplesmente REST.
Serviços que possuem essa característica de (sufixo ful, em inglês) Web podem ser chamados de RESTful.
Conceitos HTTP
Recurso
Coração do REST
https://cursos.alura.com.br/forum/subcategoria-java/todos/1
amqp://eats:caelum123@rabbitmq:5672
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==
Representações
Mesmo ingrediente, outro sabor: JSON
URL de um livro da Casa do Código
- https://www.casadocodigo.com.br/products/livro-git-github
{
"nome": "Controlando versões com Git e GitHub",
"autores": [
{ "autor": "Alexandre Aquiles" },
{ "autor": "Rodrigo Caneppele" }
],
"paginas": 220,
"ISBN": "978-85-66250-53-4"
}Representações
Mesmo ingrediente, outro sabor: XML
Mesma URL de um livro da Casa do Código
- https://www.casadocodigo.com.br/products/livro-git-github
<livro>
<nome>Controlando versões com Git e GitHub</nome>
<autores>
<autor>Alexandre Aquiles</autor>
<autor>Rodrigo Caneppele</autor>
</autores>
<paginas>220</paginas>
<ISBN>978-85-66250-53-4</ISBN>
</livro>Representações
Media Types
Tipos comuns:
text/html-> HTMLtext/plain->texto puroimage/png-> imagens PNGapplication/json-> JSONapplication/xml-> XMLapplication/pdf-> PDFapplication/epub+zip-> ebooks EPUBapplication/vnd.amazon.mobi8-ebook-> ebooks MOBIapplication/vnd.ms-excel-> arquivos.xlsExcel
https://www.vivo.com.br/faturas/2024-12
O que o servidor fará se você fizer uma requisição
para a URL acima?
🤔
GET
Usado para obter uma representação de um recurso em uma determinada URL.
POST
Cria um novo recurso no servidor, com sua própria URL.
Métodos
O que deve-se fazer com o recurso
PUT
Atualiza ou cria um recurso na URL informada.
DELETE
O recurso da URL informada é removido do servidor.
PATCH
Atualiza uma representação parcial do recurso da URL informada.
HEAD
Usado para obter os metadados de um recurso (sem a sua representação).
OPTIONS
Retorna os métodos HTTP suportados por uma URL.
TRACE
Repete o request, para verificar se alguma alteração foi feita por intermediário.
CONNECT
Request vira um túnel TCP/IP para permitir comunicação por um proxy.
GET https://www.vivo.com.br/faturas/2024-12
Como dizer para o servidor que eu quero
a representação em XML e não em JSON?
🤔
Accept
Enumera os formatos aceitos como resposta.
Access-Control-Allow-Origin
Enviado na resposta para indicar quais origins podem acessar um recurso.
Cabeçalhos
Guiando a conversa entre cliente e servidor
Authorization
Transporta credenciais de autenticação na requisição.
Content-type
Indica a representação usada na requisição e na resposta
Location
Indica, na resposta, para indicar redirecionamento ou localização de recurso.
POST https://www.vivo.com.br/faturas
Como o cliente vai saber que foi criada uma nova fatura?
🤔
1XX (Informational)
A requisição foi recebida e o processamento continua
Status code
Feedback para o cliente
2XX (Success)
A requisição foi recebida, entendida e aceita com sucesso
3XX (Redirection)
Mais ações são necessárias para completar a requisição
4XX (Client error)
A requisição é inválida e contém erros causados pelo cliente
5XX (Server error)
O servidor falhou em completar uma requisição válida.
Juntando tudo...
Comunicação completa



Richardson Maturity Model
Avaliando o quanto uma API está aderente ao REST
Nível 0
Pântano de POX (Plain Old XML)

Nível 1
Recursos

Nível 2
Métodos HTTP

Nível 3
Controle de Hypermedia (hyperlinks)
Assunto para a próxima 😎
Mão na massa...
O que é Dart
O que é o Dart?
- Desenvolvida pelo Google
- Otimizada para o desenvolvimento web e móvel
- Compilação Ahead-of-Time (AOT)
- Desempenho Consistente, inicialização rápida e eficiência de memória
- Just-in-Time (JIT)
- Otimizações dinâmicas, ideal para desenvolvimento
-
Multiparadigma
- Orientação a objetos e funcional
Curiosidade: Foi criada para superar as limitações do JavaScript e melhorar a performance das aplicações web.
Sintaxe básica
Tipagem e main
/*
* A função main é o ponto de entrada de uma aplicação Dart.
*/
void main() { // blocos delimitados por chaves
// Indentação com dois espaços
print("Meu primeiro programa em Dart.");
// Estaticamente tipada
int titulosEmCopa = 5;
double idhBrasil = 0.759;
String nome = "Brasil";
// Tem inferência de tipos
var palmeirasTemMundial = false;
print(palmeirasTemMundial.runtimeType); // bool
print('O Palmeiras tem ' + (palmeirasTemMundial ? 'um' : 'nenhum') + ' mundial.');
// Interpolação de strings
print('O $nome tem $titulosEmCopa títulos de Copa do Mundo e um IDH de $idhBrasil.');
}Sintaxe básica
Padrão de codificação e nomenclatura
// Classes e enums em PascalCase
class PlanoTelefonico {
}
enum TipoPlano { basico, premium }
// Constantes em MAIÚSCULAS_COM_UNDERLINE
const double TAXA_ADESAO = 99.99;
// Variáveis, funções e parâmetros em camelCase
void calcularTarifaPlano(String nomePlano, String cliente) {
var valorMensal = 50.0;
// Indentação com 2 espaços
// Linhas com limite de 80 caracteres.
if (nomePlano == 'Vivo Pós 40GB com Netflix' && cliente == 'Cácio') {
darDescontoEspecial(99.99, cliente);
// Espaço antes das chaves dos blocos e nos parâmetros
}
}Condicionais e laços
IF - ELSE
if (minutosDeLigacao > 100) {
print('Você ultrapassou o limite de minutos.');
} else if (minutosDeLigacao > 50) {
print('Você está perto de atingir o limite de minutos.');
} else {
print('Você está dentro do limite de minutos.');
}
for (int i = 0; i < 5; i++) {
print('Canal $i: ${canais[i]}');
}
int contador = 0;
while (contador < 5) {
print('Promoção ativa para o plano $plano');
contador++;
}
FOR - WHILE
Condicionais e laços
Switch, switch expressions e exhaustiveness checking
String plano = 'Premium';
switch (plano) {
case 'Basico':
print('Plano Básico: Acesso limitado aos canais.');
case 'Premium':
print('Plano Premium: Acesso a todos os canais.');
default:
print('Plano desconhecido.');
}
String descricaoPlano = switch (plano) {
case 'Basico' => 'Plano Básico: Acesso limitado aos canais.',
case 'Premium' => 'Plano Premium: Acesso a todos os canais.',
default => 'Plano desconhecido.',
};
print(descricaoPlano);
enum TipoPlano { basico, premium, executivo }
TipoPlano tipo = TipoPlano.premium;
// Dart garante que todos os casos sejam cobertos
switch (plano) {
case TipoPlano.basico:
print('Plano Básico: Acesso limitado aos canais.');
case TipoPlano.premium:
print('Plano Premium: Acesso a todos os canais.');
case TipoPlano.executivo:
print('Plano Executivo: Benefícios exclusivos.');
}
Null Safety
Declaração e atribuição
// Não pode ser nulo
String nomeAssinante = 'Ana';
nameAssinante = null; ❌
// Pode ser nulo
String? planoAssinante = 'Premium';
planoAssinante = null; ✅
-
Null é uma das principais causas de falhas em tempo de execução
-
Variável nula deve ser explicitamente declarada
Null Safety
Operadores
// (??) -> Null Coalescing Operator
String? nomePlano;
String escolhido = nomePlano ?? 'Vivo Pós';
print(escolhido); // Saída: Vivo Pós
// (??=) -> Null Coalescing Assignment Operator
List<String>? canais = ['HBO', 'ESPN', 'Warner'];
canais ??= ['Globo', 'SBT', 'Record'];
print(canais); // Saída: [HBO, ESPN, Warner]
// (?.) -> Conditional Access Operator
String? assinante;
// Não lança exceção, apenas retorna null
print(assinante?.length);
// (!) -> Null Assertion Operator
print(assinante!); // Lança uma exceção se assinante for nuloFunções
Definição e parâmetros posicionais
void exibirLogo() {
print('Sou a logo da VIVO');
}
exibirLogo(); // invoca função
// Os dois parâmetros são obrigatórios
void realizarAssinatura(String nome, String plano) {
print('Cliente $nome assinou o plano $plano');
}
// Passa parâmetros pela posição
realizarAssinatura('Bia', 'Ultra HD');-
Precisa especificar o tipo de retorno
-
void quer dizer que não retorna valor
-
-
Parâmetros posicionais:
-
são obrigatórios
-
não podem ter valor padrão
-
Funções
Parâmetros nomeados
double calculaDesconto(
double valorDoPlano,
{ String? categoria, bool comFidelidade = false }
) {
double desconto = 0.0;
if (categoria == 'V') {
desconto = 0.2;
} else if (categoria == 'Platinum') {
desconto = 0.15;
} else if (categoria == 'Gold') {
desconto = 0.10;
}
if (comFidelidade) {
desconto += 0.05;
}
return valorDoPlano - (valorDoPlano * desconto);
}-
São opcionais por padrão, a menos que estejam anotados com required
-
Podem receber valor padrão
-
Podem ser passados em qualquer ordem
double calculaDesconto(
double valorDoPlano,
{ String? categoria, bool comFidelidade = false }
) {
// código omitido
}
var descontoZero = calculaDesconto(100);
var descontoV = calculaDesconto(100, categoria: 'V');
var descontoFidelidade = calculaDesconto(100, comFidelidade: true);
var descontoPlatinum = calculaDesconto(100, categoria: 'Platinum', comFidelidade: true);Funções
Função anônima
List<String> canais = ['SporTV', 'HBO', 'Warner'];
/*
* Função anônima (lambda) passada como argumento.
* Repare que ela tem corpo delimitado por chaves.
*/
canais.forEach((String canal) {
print(canal);
});
// Lambda com uma linha pode usar arrow ( => )
canais.forEach((String canal) => print(canal));-
Podem ter bloco delimitado por chaves
-
Lambdas com uma linha podem usar expression body (arrow =>)
Orientação a objetos
Classes, atributos e construtores
// Definição de classe com keywork class
class Assinante {
// atributos
String nome;
String plano;
// Generative construtor
Assinante(this.nome, this.plano);
@override
String toString() => 'Nome: $nome, Plano: $plano';
}
var assinante = Assinante('Ana', 'Básico');
print(assinante); // 'Nome: Ana, Plano: Básico
assinante.plano = 'Premium';
print(assinante.plano); // Premium-
Atributos não nulos precisam ser inicializados
-
Generative constructor é um atalho para inicializar os atributos
-
valem as mesmas regras de atributos posicionais e nomeados das funções
-
-
Atributos geram getter e setter (quando pertinente) implícitos
Orientação a objetos
Encapsulamento e construtores nomeados
class Celular {
String _marca;
String _modelo;
String _numero;
// Getters
String get marca => _marca;
String get modelo => _modelo;
String get numero => _numero;
// Setters
set marca(String novaMarca) => _marca = novaMarca;
set modelo(String novoModelo) => _modelo = novoModelo;
set numero(String novoNumero) => _numero = novoNumero;
// Construtor nomeado usando Initializing List
Celular.fromJson(Map<String, dynamic> json)
: _marca = json['marca'],
_modelo = json['modelo'],
_numero = json['numero'];
@override
String toString() => 'Celular{_marca: $_marca, _modelo: $_modelo, _numero: $_numero}';
}
Map<String, String> json = {
'marca': 'Vivo',
'modelo': 'Xiaomi Mi 11',
'numero': '11987654321'
};
Celular celular = Celular.fromJson(json);
print(celular);
celular.marca = 'Samsung';
celular.modelo = 'Galaxy S21';
celular.numero = '11912345678';
print(celular);-
Atributos com prefixo `_` são privados
-
Use get e set para criar métodos acessores
-
Dart suporta construtores nomeados que inicializam os atributos (initializing list)
Orientação a objetos
Herança
class Plano {
String nome;
Plano(this.nome);
}
/*
* Palavra chave EXTENDS permite
* usar herança no Dart.
*/
class PlanoFamilia extends Plano {
int membros;
PlanoFamilia(String nome, this.membros) : super(nome);
void mostrarInfo() {
print('Plano: $nome, Membros: $membros');
}
}
PlanoFamilia planoFamilia = PlanoFamilia('Vivo Família', 4);Processamento assíncrono
Future
// Simulando uma operação assíncrona de verificação de crédito
Future<String> verificarCredito() {
return Future.delayed(Duration(seconds: 3), () {
return 'Crédito aprovado';
});
}
void main() {
print('Verificando crédito do cliente...');
verificarCredito()
.then((status) {
print('Status do crédito: $status');
});
}- Assincronismo permite realizar operações sem bloquear o fluxo principal do programa
- Future representa um valor ou erro que estará disponível no futuro
- quando o valor estiver disponível, invoca o callback passado no then
Processamento assíncrono
Async await
// Repare na palavra ASYNC
Future<String> verificarCredito() async {
await Future.delayed(Duration(seconds: 3));
return 'Crédito verificado';
}
void main() async {
print('Verificando crédito do cliente...');
String status = await verificarCredito();
print('Status do crédito: $status');
}- Async/await facilitam a escrita de códigos que possuem assincronismo
-
Escolha alguns sistemas que se integram dentro da Vivo;
-
Desenhe o fluxo de comunicação entre esses sistemas (ESB, ponto a ponto)
-
Faça uma engenharia reversa e documente parte dessa API
-
Endpoints
-
Operações
-
Estruturas de dados
-
Protocolo
-
Desafio
Nossas aulas
Anterior
Próxima
Aula 3
12/12 às 10h
Design de APIs RESTful
Atual
Aula 1
11/12 às 11h
→ Introdução às APIs
Aula 2
11/12 às 15h
→ Fundamentos do REST
Aula 3
Design de APIs RESTful
1
HATEOAS
(Hypermedia)
2
Boas práticas
3
Documentação e Swagger
5
Desafio
4
Mão na massa
O poder dos links
Navegação
Links para recursos relacionados
{
"id": "12345",
"nome": "João Silva",
"email": "joao.silva@exemplo.com",
"telefone": "+55 11 91234-5678",
"_links": {
"self": {
"href": "https://api.vivo.com.br/clientes/12345"
},
"planos": {
"href": "https://api.vivo.com.br/clientes/12345/planos"
},
"dispositivos": {
"href": "https://api.vivo.com.br/clientes/12345/dispositivos"
},
"faturas": {
"href": "https://api.vivo.com.br/clientes/12345/faturas"
}
}
}
representação HAL+JSON
Máquina de estado
Links de transição

HATEOAS
{
"id":1,
"valor":51.80,
"nome":"ANDERSON DA SILVA",
"numero":"1111 2222 3333 4444",
"expiracao":"2022-07",
"codigo":"123",
"status":"CRIADO",
"formaDePagamentoId":2,
"pedidoId":1,
"_links":{
"self":{
"href":"http://localhost:8081/pagamentos/1"
},
"confirma":{
"href":"http://localhost:8081/pagamentos/1"
},
"recusa":{
"href":"http://localhost:8081/pagamentos/1"
},
"cancela":{
"href":"http://localhost:8081/pagamentos/1"
}
}
}
Transição explícita no próprio recurso
Mão na massa...
Boas práticas
Boas práticas de design
Princípios
- Consistência
- Simplicidade
- Flexibilidade
- Segurança
"A good API is not just a set of endpoints, but a carefully crafted interface that empowers developers."
Autor desconhecido
Nomenclatura de recursos
Recursos são coisas
| Ruim | Bom |
|---|---|
| /getUsers | /users |
| /article_by_id?id=123 | /articles/{id} |
| /get-pending-orders | /orders?status=pending |
- Coleções no plural
- Evita-se verbos na URL
- descreve recurso, não a ação
- Filtros via query string
Métodos e Status
Semântica importa
| Método | Uso típico | Códigos comuns |
|---|---|---|
| GET | Recuperar | 200, 404 |
| POST | Criar | 201, 400 |
| PUT | Atualizar | 200, 204 |
| DELETE | Remover | 200, 204, 404 |
Versionamento
Estratégias e semântica
Prós e contras
Local da versão:
- URI:
/v1/users - Query Param:
/users?version=1 - Header: Accept:
application/vnd.company.v1+json
Semântica:
- 1.0.0 -> 1.0.1 (correção de bug)
- 1.0.0 -> 1.1.0 (nova funcionalidade compatível)
- 1.0.0 -> 2.0.0 (mudanças incompatíveis)
Documentação
OpenApi e Swagger
openapi: 3.0.0
info:
title: Sample API
version: 1.0.0
paths:
/users:
get:
summary: Returns a list of users
responses:
'200':
description: Successful response
Transição explícita no próprio recurso
Exemplo de contrato OpenAPI
- Documentação interativa
- Gerada automaticamente
Mão na massa...
void main() {
runApp(
MaterialApp(
home: Text('Hello World'),
),
);
}
MaterialApp
Column
-
Widget raiz de um aplicativo que segue o Material Design
-
Fornece estrutura básica e configurações essenciais
-
Permite navegação entre telas
-
Aplica as diretrizes do Material Design
-
e mais...
/**
* Representa o esqueleto de uma tela
* (AppBar, body, FloatActionButton, Drawer,
* BottomNavigationBar, etc...)
*/
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Histórico de faturas'),
),
body: Center(
child: Text('Mensagem de dentro de um Scaffold')
),
)
),
);
}
Scaffold
Column

void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Histórico de faturas'),
),
/**
* CARD Representa um cartão de material design.
* Sombra, cantos arredondados.
* Diferente do Container, que tem propósito
* genérico.
*/
body: Card(
color: Colors.white,
/**
* ListTile exibe informações de forma
* organizada e interativa.
*/
child: ListTile(
leading: Icon(Icons.check_circle, color: Colors.green),
title: Text('Venceu em 25/01'),
subtitle: Text('Paga'),
trailing: Text("R\$ 149.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)),
),
),
),
),
);
}
Card e ListTile
Column

Column(
children: [
Card(
color: Colors.white,
child: ListTile(
leading: Icon(Icons.watch_later_outlined, color: Colors.grey),
title: Text('Vence em 24/03'),
subtitle: Text('Aberta'),
trailing: Text("R\$ 153.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)
),
),
),
Card(
color: Colors.white,
child: ListTile(
leading: Icon(Icons.watch_later_outlined, color: Colors.red),
title: Text('Venceu em 25/03'),
subtitle: Text('Atrasada'),
trailing: Text("R\$ 153.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)
),
),
),
Card(
color: Colors.white,
child: ListTile(
leading: Icon(Icons.check_circle, color: Colors.green),
title: Text('Venceu em 25/02'),
subtitle: Text('Paga'),
trailing: Text("R\$ 146.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)),
),
),
Card(
color: Colors.white,
child: ListTile(
leading: Icon(Icons.check_circle, color: Colors.green),
title: Text('Venceu em 25/01'),
subtitle: Text('Paga'),
trailing: Text("R\$ 149.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)),
),
),
],
);
Column (e Row)
Column

SingleChildScrollView(
child: Column(
children: [
Card(
color: Colors.white,
child: ListTile(
leading: Icon(Icons.watch_later_outlined, color: Colors.grey),
title: Text('Vence em 24/03'),
subtitle: Text('Aberta'),
trailing: Text("R\$ 153.00",
style: TextStyle(color: Colors.black, fontSize: 20.0)
),
),
),
// ... muitos outros Cards
],
),
);
Overflow e SingleChildScrollView
Column

void main() {
runApp(
Center(
child: Text(
'Olá, Mundo Flutter!',
textDirection: TextDirection.ltr,
),
),
);
}
Widgets Próprios
-
Criados com widgets de baixo nível
-
Retornam a representação visual no método build
-
Podem encapsular uma lógica de apresentação
-
Trazem semântica e reaproveitamento
-
Estendem StatelessWidget ou StatefulWidget
void main() {
runApp(OlaMundo());
}
class OlaMundo extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Text('Olá, Mundo Flutter!', textDirection: TextDirection.ltr),
);
}
}

StatelessWidget
-
Não rastreiam valores ao longo do ciclo de vida
-
Não atualizam a tela com mudanças de estado
-
Encapsulam uma lógica de apresentação
-
Geralmente são imutáveis
-
Reduzem código duplicado e melhoram a coesão

StatelessWidget
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Scaffold(
appBar: AppBar(
title: Text('Histórico de faturas'),
),
body: Column(
children: [
Card(
color: Colors.white,
child: ListTile(
leading: Icon(
Icons.watch_later_outlined,
color: Colors.grey,
),
title: Text('Vence em 24/03'),
subtitle: Text('Aberta'),
trailing: Text(
"R\$ 153.00",
style: TextStyle(color: Colors.black, fontSize: 20.0),
),
),
),
// ... demais Cards
],
),
),
),
);
}
void main() {
runApp(
MaterialApp(
theme: ThemeData(useMaterial3: true),
home: Scaffold(
appBar: AppBar(
title: Text('Histórico de faturas'),
),
body: Column(
children: [
CardFatura(153.0, '25/04', aberta: true,),
CardFatura(146.0, '25/03', atrasada: true),
CardFatura(146.0, '25/02'),
CardFatura(146.0, '25/02'),
],
),
),
),
);
}
class CardFatura extends StatelessWidget {
double valor;
String vencimento;
bool aberta;
bool atrasada;
CardFatura(this.valor, this.vencimento,
{this.aberta = false, this.atrasada = false, Key? key})
: super(key: key);
@override
Widget build(BuildContext context) {
String status = 'Paga';
Icon icone = Icon(Icons.check_circle, color: Colors.green);
if (atrasada) {
icone = Icon(Icons.watch_later_outlined, color: Colors.red);
status = 'Atrasada';
} else if (aberta) {
icone = Icon(Icons.error_outline, color: Colors.grey);
status = 'Aberta';
}
return Card(
color: Colors.white,
child: ListTile(
leading: icone,
title: Text('Venceu em $vencimento'),
subtitle: Text(status),
trailing: Text("R\$ ${valor.toStringAsFixed(2)}",
style: TextStyle(color: Colors.black, fontSize: 20.0)),
),
);
}
}

Mão na massa...
StatefulWidget
-
Possuem estado que muda com ações do usuário ou eventos externos
-
Repintam a tela para exibir o estado atualizado
-
Estado encapsulado numa classe separada do widget

StatefulWidget
class JogoDeDados extends StatefulWidget {
@override
JogoDeDadosState createState() => JogoDeDadosState();
}
class JogoDeDadosState extends State<JogoDeDados> {
String dado1 = '?';
String dado2 = '?';
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container( // Representa o primeiro dado
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: Text(dado1, // Texto do dado 1
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
),
),
SizedBox(width: 8),
Container( // Representa o segundo dado
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
),
child: Center(
child: Text(dado2, // Texto do dado 2
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
),
),
],
),
SizedBox(height: 24),
ElevatedButton(
onPressed: sorteia, // Precisa atualizar a tela
child: Text('Rolar dados'),
),
],
);
}
void sorteia() {
setState(() { // Autaliza o estado para atualizar a tela
dado1 = '${Random().nextInt(6) + 1}';
dado2 = '${Random().nextInt(6) + 1}';
});
}
}
Dúvida! Quem atualiza? A tela ou o dado? 🤔

A tela pode se atualizar com widgets stateless.
Pode ser stateless, reaproveitado e evitar código duplicado.
StatefulWidget + StatelessWidget
class JogoDeDados extends StatefulWidget {
@override
JogoDeDadosState createState() => JogoDeDadosState();
}
class JogoDeDadosState extends State<JogoDeDados> {
String dado1 = '?';
String dado2 = '?';
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [Dado(dado1), SizedBox(width: 8), Dado(dado2)],
),
SizedBox(height: 24),
ElevatedButton(
onPressed: sorteia,
child: Text('Rolar dados'),
)
],
);
}
void sorteia() {
setState(() {
dado1 = '${Random().nextInt(6) + 1}';
dado2 = '${Random().nextInt(6) + 1}';
});
}
}
class Dado extends StatelessWidget {
final String numero;
const Dado(this.numero);
@override
Widget build(BuildContext context) {
return Container(
height: 100,
width: 100,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.2),
blurRadius: 6,
offset: Offset(0, 3),
),
],
),
child: Center(
child: Text(
numero,
style: TextStyle(
fontSize: 50,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
- Crie um serviço simples para gerenciar os planos da Vivo
- Integre o serviço de fatura com o serviço de Plano para pegar detalhes do nome e do valor
Desafio
Nossas aulas
Anterior
Próxima
Aula
12/12 às 15h
Autenticação e Segurança de APIs REST
Aula 3
12/12 às 10h
Design de APIs RESTful
Atual
Aula 2
11/12 às 15h
→ Fundamentos do REST
Aula 4
Autenticação e Segurança de APIs REST
1
Segurança em camadas
2
Zero Trust
3
Cuidados nas APIs
5
Desafio
4
Cuidados no ambiente de execução
Malha de serviços
Visão geral das integrações







Tradicional
- Confia no perímetro
- Verifica uma vez
- IP interno = confiável
- Acesso amplo
Zero Trust Model
Mudando a mentalidade
Zero Trust
- Não confia em nada
- Verifica continuamente
- Localização irrelevante
- Menor privilégio possível
Zero Trust Model
Checklist da aplicação
Integração com OAuth e JWT
- OAuth permite autorização granular
- JWTs com claims específicos para autorização
- Tokens com vida útil curta
Considerações da implementação
- Evitar cookies para autenticação
- Habilitar CORS
- Implementar rigoroso controle de acesso
- Validar dados de entrada
Mão na massa...
Cuidados da aplicação
Exemplos de ataques
Command Injection
Descrição:
Ocorre quando a entrada do usuário é utilizada em comandos de sistema operacional, permitindo que o atacante execute comandos arbitrários.
Impacto:
- Controle completo do servidor.
- Roubo de dados, modificação de arquivos, ou ataques adicionais à infraestrutura.
Resultado:
Arquivos críticos do sistema podem ser deletados.
ENTRADA: & rm -rf /Exemplos de ataques
Cross-Site Scripting (XSS)
Descrição:
Acontece quando entradas maliciosas, como scripts JavaScript, são refletidas na resposta da API ou armazenadas para serem exibidas posteriormente.
Impacto:
- Roubo de sessões e credenciais do usuários
- Execução de scripts maliciosos no navegador do usuário.
- Redirecionamento para sites maliciosos.
Resultado:
O script é executado no navegador de outro usuário.
<script>alert('Hacked');</script>Exemplos de ataques
Remote Code Execution (RCE)
Descrição:
Acontece quando entradas maliciosas, como scripts JavaScript, são refletidas na resposta da API ou armazenadas para serem exibidas posteriormente.
Resultado
- Controle completo do servidor.
- Escalação de privilégios.
- Uso do servidor para atividades maliciosas (ex.: mineração de criptomoedas, envio de spam).
GET https://server.com?controller=TarefasController&acao=listaTarefasDoUsuario
<?php
$controller = new $_GET['controller'];
$metodo = $_GET['acao']
$controller->$metodo()Exemplos de ataques
Path Injection
Descrição:
Ocorre quando um caminho (path) fornecido pelo usuário é concatenado diretamente para manipular arquivos ou URLs.
Impacto:
- Modificação de dados sensíveis.
- Exposição de arquivos internos.
ENTRADA: /var/data/../../etc/passwdExemplos de ataques
JSON/XML Injection
Descrição:
Ocorre quando entradas maliciosas são usadas para manipular o parser de JSON ou XML da API.
Impacto:
- Corrupção ou manipulação de dados.
- Acesso a arquivos locais ou requisições para servidores internos.
Resultado:
O conteúdo de /etc/passwd é exposto ao atacante.
<!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd"> ]>
<data>&xxe;</data>Validações de dados de entrada
Diretrizes
-
Validação e Sanitização de Dados:
- Valide sempre o tipo, formato e tamanho das entradas.
- Remova caracteres especiais ou use codificação segura.
-
Use Bibliotecas Confiáveis:
- Utilize bibliotecas de framework para proteção contra injeções (ex.: Prepared Statements para SQL).
-
Limite o Tamanho da Requisição:
- Configure limites de tamanho para o corpo das requisições.
-
Restrinja Diretórios e Caminhos:
- Nunca concatene diretamente caminhos fornecidos pelo usuário.
-
Desabilite Recursos Inseguros:
- Desabilite suporte a entidades externas em parsers XML.
-
Log e Monitoramento:
- Registre tentativas de entrada maliciosa e monitore padrões incomuns.
-
Validação de Tipo e Estrutura:
- Use schemas para validar JSON/XML, como JSON Schema ou ferramentas de validação de contratos.
Cuidados com o ambiente de execução
Práticas de segurança
Zero Trust na Infra
Use Imagens de Container Seguras:
- Sempre use imagens oficiais ou de fontes confiáveis.
- Digitalize imagens antes de usá-las para verificar vulnerabilidades.
- Mantenha as imagens atualizadas regularmente.
Ferramentas:
Docker Security ScanTrivyClair.
Práticas de segurança
Zero Trust na Infra
Restringir Privilégios do Container:
-
Execute containers com o menor privilégio possível (non-root container):
- Não use o usuário
rootdentro do container. - Use a flag
--userpara definir um usuário não privilegiado.
- Não use o usuário
- Desative capacidades perigosas do Linux, como
CAP_SYS_ADMIN.
Exemplo:
securityContext:
runAsUser: 1000
runAsGroup: 1000
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
Práticas de segurança
Zero Trust na Infra
Gerencie Segredos com Segurança:
- Não armazene segredos diretamente dentro do container.
- Use gerenciadores de segredos, como AWS Secrets Manager, HashiCorp Vault, ou Kubernetes Secrets.
- Sempre criptografe segredos em trânsito e em repouso.
Exemplo:
apiVersion: v1
kind: Secret
metadata:
name: db-secret
data:
username: dXNlcm5hbWU=
password: cGFzc3dvcmQ=Práticas de segurança
Zero Trust na Infra
Isolamento Adequado:
- Use namespaces e control groups (
cgroups) para isolar containers no nível do sistema operacional.
Ferramentas:
PodSecurityPolicies
Validação de Configurações:
- Verifique configurações do Docker e Kubernetes com ferramentas como
kube-benchoudocker-bench-security.
Práticas de segurança
Zero Trust na Infra
Implementar Logs e Monitoramento adequados:
- Configure o logging adequado para monitorar atividades anômalas.
- Monitore integração de serviços com OpenTracing
Ferramentas:
FluentdPrometheusGrafanaELK StackZipkin
E não para por aqui...
Como é a navegacão no Flutter?
Column
Tela 1
Tela 2
Tela 3
Tela N
push
push
push
pop
pop
pop
popUntil
Funciona como uma pilha!

Eu vejo Futures ao
mudar de tela
Com que frequência?
O tempo todo!
Navegação anônima
Mais simples e imperativas.
Formas de navegação
Navegação nomeada
Declaradas no atributo `router` de MaterialApp.
Navegação 2.0
Mais complexa e completa. Ideal para aplicativos web.
Rotas anônimas
- Mais fáceis de passar parâmetros
na navegação - Rotas ficam "escondidas" e
espalhadas pelo código - Usam MaterialPageRoute para
fazer a transição
Navegação
/**
* NAVIGATOR é o objeto responsável pela
* navegação no aplicativo.
*/
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => TelaDeCobranca(),
));// Volta para a tela anterior
Navigator.of(context).pop();
Rotas nomeadas
- Rotas declaradas no MaterialApp
- São facilmente encontradas
- Passagem de parâmetros exige
casting - Não precisa do MaterialPageRoute
na navegação
Navegação
// Navegação mais simplificada
Navigator.of(context).pushNamed('/fatura');
Navigator.of(context).pop();
MaterialApp(
routes: {
'/': (context) => TelaInicial(),
'/faturas': (context) => ListagemDeFaturas(),
'/faturas/cobranca': (context) => CobrancaDeFatura(),
},
);
Rotas anônimas
- Diretamente no construtor da
nova tela - No método `pop` ao voltar para
a tela anterior
Passagem de parâmetros
Navigator.of(context)
.push(MaterialPageRoute(
builder: (context) => TelaDeCobranca(fatura),
))
.then((sucesso) => {
if (sucesso) {
retirarNomeDoSerasa();
} else {
tentarNovaNegociacao();
}
);// Em algum lugar em TelaDeCobranca
Navigator.of(context)
.pop(pagamentoRealizadoComSucesso);
Rotas nomeadas
- Diretamente no Navigator (Object)
- Parâmetros disponíveis em
ModalRoute - Precisa de casting
Passagem de parâmetros
Navigator.pushNamed(context, '/fatura/cobranca',
arguments: {'id': 42}
)
.then((sucesso) => {
if (sucesso) {
retirarNomeDoSerasa();
} else {
tentarNovaNegociacao();
}
);// Em algum lugar em TelaDeCobranca
final args = ModalRoute.of(context)!.settings.arguments as Map;
int id = args['id'];
Navigator.of(context)
.pop(pagamentoRealizadoComSucesso);
- Execute o keycloak e import o realm do arquivo "realm-desenvolvimento.json"
- Configure os serviços para validarem tokens
Desafio
Nossas aulas
Anterior
Próxima
Aula
16/12 às 10h
GraphQL
Atual
Aula 3
12/12 às 10h
Design de APIs RESTful
Aula
12/12 às 15h
Autenticação e Segurança de APIs REST
Aula 5
GraphQL

Linha do tempo
2012
Criada internamente no Facebook
2015
Projeto
OpenSource
2018
GraphQL
Foundation
2024
Adoção em massa

Todo poder emana do cliente!

GraphQL vs REST


Demo rápida

Schema, tipos e operações

Coração do GraphQL
type Cliente {
id: ID!
nome: String!
email: String!
faturas: [Fatura!]!
}
type Fatura {
id: ID!
valor: Float!
vencimento: String!
clienteId: ID!
status: StatusFatura!
}
enum StatusFatura {
ABERTA
ATRASADA
PAGA
}Tipos complexos
type Query {
consultaCliente(id: ID!): Cliente
pesquisaFaturasDoCliente(clienteId: ID!): [Fatura!]!
pesquisaFaturasPorStatus(
clienteId: ID!,
StatusFatura: status!
): [Fatura!]!
}
type Mutation {
cadastraCliente(
cliente: NovoClienteInput!
): Cliente
registraPagamento(faturaId: ID!): Boolean!
}
input NovoClienteInput {
nome: String!
email: String!
}Definição de operações
Anatomia de um comando

Payload e retorno
type Query {
consultaCliente(id: ID!): Cliente
}Definição da Query
query {
consultaCliente(id: "1") {
# Campos que deseja receber (não listou email)
id
nome
faturas {
valor
status
}
}
}Payload enviado
Resolvers e Netflix DGS

Implementando a busca
query {
consultaCliente(id: "1") {
id
nome
faturas {
valor
status
}
}
}Payload enviado
Payload enviado
@DgsComponent
public class ClientePorId {
private ClienteRepository clienteRepo;
private FaturaRespository faturaRepo;
@DgsQuery
public Cliente consultaCliente(@InputArgument Long id) {
return clienteRepo.getReferenceById(id);
}
@DgsData(parentType = "Cliente", field = "faturas")
public List<Fatura> buscaFaturasDoCliente(DgsDataFetchingEnvironment env) {
Cliente cliente = env.getSource();
return faturaRepo.findByClienteId(cliente.getId());
}
}Implementação no Spring Boot
Mão na massa...


Imagens
Imagens
- Carrega imagens da internet via URL
- Problema se estiver sem conexão
- Bom para ambiente de desenvolvimento
Image.network
Image.network(
'https://pbs.twimg.com/media/Fwvbm_NWAAIpIOV?format=jpg&name=large',
width: 300,
height: 450,
);
Imagens
- Meio mais seguro
- Deixa o app maior
Image.asset
flutter:
# Tema que descomentar essa linha no
# adicionar os recursos
assets:
- assets/images/flu_serie_b.jpgImage.asset('assets/images/flu_serie_b.jpg');
pubspec.yaml
Imagens
- Carrega arquivo local do dispositivo
- Ideal para abrir da galeria de fotos ou
captura de câmera - Requer permissão
- Tipo `File
Image.file
- Exibe imagens carregadas em bytes na
memória - Útil para exibir imagens recuperadas de APIs
- Tipo `Uint8List`
Image.memory
Imagens
- Carrega arquivo local do dispositivo
- Ideal para abrir da galeria de fotos ou
captura de câmera - Requer permissão
- Tipo `File
Image.file
- Exibe imagens carregadas em bytes na
memória - Útil para exibir imagens recuperadas de APIs
- Tipo `Uint8List`
Image.memory
Temas
Estrutura do tema (M3)
MaterialApp(
title: 'Meu App',
theme: ThemeData(
useMaterial3: true,
),
home: MyHomePage(),
);
ThemeData
-
ThemeData encapsula as propriedades do tema
-
MaterialApp aplica o tema a todos os widgets descendentes.
-
`useMaterial3: true` para habilitar os novos componentes e estilos
Principais componentes
MaterialApp(
title: 'Meu App',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(seedColor: Color(0xFF8533AD)),
// Roxinho VIVO
),
home: MyHomePage(),
);
Cores e ColorScheme
-
Novo sistema de cores que facilita a criação de paletas dinâmicas
-
ColorScheme completo a partir de uma cor base (seed color)
-
Promove uma estética mais coesa
Principais componentes
MaterialApp(
title: 'Meu App',
theme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Color(0xFF8533AD)
// Roxinho VIVO
),
textTheme: TextTheme(
displayLarge: TextStyle(
fontSize: 32, fontWeight: FontWeight.bold
),
bodyLarge: TextStyle(
fontSize: 18, color: Colors.black87
),
),
),
home: MyHomePage(),
);
Tipografia e TextTheme
-
Nova abordagem de tipografia com TextTheme
-
Estilos flexíveis e consistentes.
Principais componentes
MaterialApp(
title: 'Meu App',
theme: ThemeData(
// ... configurações anteriores
appBarTheme: AppBarTheme(
backgroundColor: Color(0xFF8533AD),
titleTextStyle: TextStyle(
color: Colors.white, fontSize: 24
),
),
filledButtonTheme: FilledButtonThemeData(
style: ButtonStyle(
padding: WidgetStateProperty.all<EdgeInsetsGeometry>(
const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
),
),
),
home: MyHomePage(),
);
Temas de componentes específicos
-
Permite temas específicos a alguns componentes
-
`appBarTheme`, `filledButtonTheme`, etc...
Principais componentes
MaterialApp(
title: 'Meu App',
theme: ThemeData(
// Configurações do tema normal
),
darkTheme: ThemeData(
useMaterial3: true,
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.red,
brightness: Brightness.dark,
),
),
themeMode: ThemeMode.dark,
// poder ser (light|system)
home: MyHomePage(),
);
Temas dinâmicos
-
Capacidade de usar cores dinâmicas
-
Adaptabilidade ao sistema operacional
-
Necessário configurar um outro ColorScheme e definir themeMode em MaterialApp
Usando tema nos widgets
Text(
'Olá, Flutter!',
style: Theme.of(context).textTheme.bodyLarge,
);
Theme.of(context)
-
Theme dá acesso ao tema adequado ao contexto do widget na árvore.
-
Reusa estilos e mantém consistência da identidade visual.
Leiautes avançados
Canais do youtube

- No serviço de pagamento:
- Criar enum para status do Pagamento
- Queries:
- listar todos os pagamentos
- pesquisar pagamentos por status
- Mutations:
- cancelar pagamento
Desafio
Nossas aulas
Anterior
Próxima
Aula
16/12 às 14h
gRPC
Aula
16/12 às 10h
GraphQL
Atual
Aula
12/12 às 15h
Autenticação e Segurança de APIs REST
Aula 6
gRPC e Protocolos Binários

Protocolos binários
Comparação com HTTP1 - Cenário 1
Serializar um objeto Curso com 5 objetos Pessoa e com um objeto Telefone associados, mostra o tamanho:
- Protocol Buffers: 250
- Thrift TCompactProcotol: 278
- Thrift TBinaryProtocol: 460
- HTTP/JSON: 559
- HTTP/XML: 836
- RMI: 905


Protocolos binários
Comparação com HTTP1 - Cenário 2
Executar 10.000 chamadas por uma listagem de códigos de Curso e, em seguida, os dados do Curso associado a esse código.
Avalia: tempo de resposta e uso de CPU no servidor e no cliente.
Tempo de resposta:
- Thrift TCompactProcotol: 01 min 05 s
- Thrift TBinaryProtocol: 01 min 13 s
- Protocol Buffers: 01 min 19 s
- RMI: 02 min 14 s
- HTTP/JSON: 04 min 44 s
- HTTP/XML: 05 min 27 s

Protocolos binários
Comparação com HTTP1 - Cenário 2 (continuação)
CPU no servidor:
- Thrift TBinaryProtocol: 33 %
- Thrift TCompactProcotol: 30 %
- Protocol Buffers: 30 %
- HTTP/JSON: 20 %
- RMI: 16 %
- HTTP/XML: 12 %
CPU no cliente:
- Thrift TCompactProcotol: 22.5 %
- Thrift TBinaryProtocol: 21 %
- Protocol Buffers: 37.75 %
- RMI: 46.5 %
- HTTP/JSON: 75 %
- HTTP/XML: 80.75 %

gRPC
Arquitetura






Tipos de canais
Todo o poder o HTTP2
Unary (request/response)
O cliente envia um request para o servidor e espera um response
Server Streaming
O cliente envia uma requisição e o servidor responde com um fluxo de dados.
Client Streaming.
O cliente envia um fluxo de dados e o servidor responde com uma única resposta.
Bidirectional Streaming
Tanto o cliente e o servidor trabalham com fluxos de dados.

Protocol Buffer
Descrevendo a comunicação

syntax = "proto3";
package plano.service; // não deve ser nome invertido
option java_package = "br.com.alura.fatura.plano"; // pacote na convenção Java
service Plano {
rpc ListaPlanos (ListaPlanosRequest) returns (RegistraPagamentoResponse) {}
rpc PlanoPorId (IntValue) returns (Plano) {}
}
message ListaPlanosRequest {
string tipo = 1;
}
message ListaPlanosResponse {
repeated Plano planos = 1;
}
message Plano {
int64 id = 1;
string nome = 2;
string tipo = 3;
}
message IntValue {
int64 value = 1;
}Implementando o serviço
Descrevendo a comunicação

@GrpcService
public class PlanoService extends PlanoServiceGrpc.PlanoServiceImplBase {
@Autowired
private PlanoRepository planoRepository;
@Override
public void listaPlnos(ListaPlanosRequest request,
StreamObserver<ListaPlanosResponse> responseObserver) {
ListaPlanosResponse response = ListaPlanosResponse.newBuilder();
planoRepository.findAll()
.map(plano -> {
return Plano.newBuilder() // mensagem gerada
.setId(plano.getId())
.setNome(plano.getNome())
.setTipo("Mensal")
.build();
})
.forEach(response::add);
responseObserver.onNext(response.build());
responseObserver.onCompleted();
}
}
Boas práticas
Considerações importantes
-
Versionamento de serviços
- Compartilhamento dos arquivos .proto
- Tratamento de erros
- Deadlines e timeouts
- Monitoramento e tracing

"Mão na massa..."
😅

1
Ciclo de vida
do Stateful
2
Instalação de
dependências
3
Provider
4
Desafio
Permissão para mudar a rota...
🫡
Formulário

-
Sempre dentro de StatefulWidget
-
Preferência para TextFormField (suporte a validação)
-
Controlado com TextEditingController
-
-
Inputs dentro de Form para controlar estado/eventos do formulário
-
Form precisa de Key para recuperar facilmente o estado
Formulário de chamados

class FormularioNovoChamado extends StatefulWidget {
FormularioNovoChamado({super.key});
@override
State<FormularioNovoChamado> createState() => _FormularioNovoChamadoState();
}
class _FormularioNovoChamadoState extends State<FormularioNovoChamado> {
final campoTitulo = TextEditingController();
final campoDescricao = TextEditingController();
final _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Novo chamado'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Título do chamado', style: TextStyle(fontSize: 28)),
TextFormField(
controller: campoTitulo,
decoration: InputDecoration(
hintText: 'Descreva o seu problema',
),
),
SizedBox(height: 40),
Text('Descreva o seu problema', style: TextStyle(fontSize: 28)),
TextFormField(
controller: campoDescricao,
decoration: InputDecoration(
hintText: 'Descreva o seu problema',
),
maxLines: 5,
),
SizedBox(height: 40),
Center(
child: FilledButton(
onPressed: () {
var chamado = criaChamado();
Navigator.of(context).pop(chamado);
},
child: const Text('Enviar chamado'),
),
),
],
),
),
),
)
);
}
Chamado criaChamado() {
return Chamado(
userId: 1,
titulo: campoTitulo.text,
dataDeAbertura: DateTime.now(),
descricao: campoDescricao.text,
);
}
}StatefulWidget++
Ciclo de vida
class CicloDeVida extends StatefulWidget {
const CicloDeVida({super.key});
@override
State<CicloDeVida> createState() => _CicloDeVidaState();
}
class _CicloDeVidaState extends State<CicloDeVida> {
/**
* Chamado uma única vez, então é o lugar ideal para
* inicializar atributos e realizar operações assíncronas.
*/
@override
void initState() { ... }
/**
* Responsável por construir a árvore de widgets.
* Também invocado sempre que o widget pai é reconstruído,
* ou quando o estado foi atualizado.
*/
@override
Widget build(BuildContext context) { ... }
/**
* Notifica que o estado interno mudou e a tela,
* a partir daquele widget, precisa ser reconstruída.
* Aciona invocação do método build.
*/
@override
void setState(VoidCallback fn) { ... }
/**
* Invocado quando o widget é removido da árvore. Usado
* para liberar recursos ou cancelar operações assíncronas.
*/
@override
void dispose() { ... }
}Mão-na-massa


FormularioDeLogin
Home
AcessoRapido
ListaDeChamados
FormularioNovoChamado
Prop drilling
Estado centralizado


Prop drilling
Estado centralizado
Provider
Instalação
ChangeNotifier
- Notifica interessados de que algo mudou
- Armazena o estado
Conceitos
ChangeNotifierProvider
- Provê acesso ao ChangeNotifier
- Deve ser colocado no último ancestral comum de onde o estado é necessário (geralmente é o APP)
Consumer
- Permite reconstruir um widget com base nos valores modificados do provider
- Deve ficar o mais baixo possível na árvore de widgets
watch e read
- Outra forma de acessar ler um estado ou reagir a mudanças nele
ChangeNotifier
class UsuarioProvider extends ChangeNotifier {
Usuario? usuario;
bool get isLogado => usuario != null;
void login(Usuario usuario) {
this.usuario = usuario;
notifyListeners();
}
void logout() {
this.usuario = null;
notifyListeners();
}
}-
Classe que guarda o estado compartilhado
-
Quando uma informação muda, precisa notificar os widgets (observadores)
ChangeNotifierProvider
class VivoApp extends StatelessWidget {
const VivoApp({super.key});
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => UsuarioProvider()
),
],
child: MaterialApp(
title: 'Vivo App',
theme: TEMA_CLARO,
home: const FormularioDeLogin(),
debugShowCheckedModeBanner: false,
),
);
}
}-
ChangeNotifierProvider cria os providers somente quando requisitados
-
Pode-se ter vários providers configurados (MultiProvider)
-
MaterialApp construído com todos os providers configurados.
Context.watch
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Vivo App'),),
body: Padding(
padding: EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 24, bottom: 36),
child: RichText(
text: TextSpan(
text: 'Olá, ',
style: TextStyle(fontSize: 24, color: Colors.black),
children: [
/**
* Precisa tomar cuidado com o CONTEXT
* que está configurado o watch
*/
TextSpan(
text: context.watch<UsuarioProvider>().usuario!.nome,
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
),
PainelChamada(),
SizedBox(height: 48),
AcessoRapido(),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print('Floating action button clicado...');
},
child: const Icon(Icons.phone),
)
);
}
}Consumer<T>
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:vivoapp/models/usuario.dart';
import 'package:vivoapp/providers/usuario_provider.dart';
import 'package:vivoapp/widgets/acesso_rapido.dart';
import 'package:vivoapp/widgets/chamada.dart';
class Home extends StatelessWidget {
const Home({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Vivo App'),),
body: Padding(
padding: EdgeInsets.all(8.0),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(top: 24, bottom: 36),
child: Consumer<UsuarioProvider>(
builder: (
BuildContext context, // Contexto diferente para otimização
UsuarioProvider provider, // o provider com os dados
Widget? child // usado para otimização
) {
return RichText(
text: TextSpan(
text: 'Olá, ',
style: TextStyle(fontSize: 24, color: Colors.black),
children: [
TextSpan(
text: '${provider.usuario!.nome}.',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
);
},
),
),
PainelChamada(),
SizedBox(height: 48),
AcessoRapido(),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print('Floating action button clicado...');
},
child: const Icon(Icons.phone),
)
);
}
}
Context.read
FilledButton(
onPressed: () {
/**
* Somente acessa, sem ser notificado de mudanças
*/
int id = context.read<UsuarioProvider>().usuario!.id;
var chamado = criaChamado(id);
Navigator.of(context).pop(chamado);
},
child: const Text('Enviar chamado'),
)Mão-na-massa
- Implementar formulário de login e disponibilizar usuário com provider.
- OBS: dados de login devem ser fictícios.
Desafio


Nossas aulas
Anterior
Próxima
Aula
17/12 às 15h
API Gateways e Backend For Frontend (BFF)
Aula
16/12 às 10h
GraphQL
Aula
16/12 às 14h
gRPC
Atual
Aula 7
API Gateways e Backend for Frontend (BFF)
Integração via HTTP
Obtendo dados da API
import 'package:http/http.dart' as http;
import 'dart:convert';
const String _BASE_URL = 'http://localhost:3000';
Future<List<Chamado>> listaChamados() async {
Uri uri = Uri.parse('$_BASE_URL/chamados');
http.Response resposta = await http.get(uri);
if (resposta.statusCode == 200) {
// Se for um objeto, seria Map<String, dynamic>
List<dynamic> listaJson = jsonDecode(resposta.body);
return listaJson.map((json) => Chamado.fromJson(json))
.toList();
} else {
debugPrint('ERRO: ${resposta.body}');
throw Exception('Erro ao buscar chamados');
}
}services/api.dart
Método GET
Convertendo em JSON
class Chamado {
// Demais meétodo e atributos
Chamado.fromJson(Map<String, dynamic> json)
: id = json['id'],
userId = json['userId'],
titulo = json['titulo'],
dataDeAbertura = DateTime.parse(json['dataDeAbertura']),
descricao = json['descricao'],
dataDeFechamento = json['dataDeFechamento'] != null
? DateTime.parse(json['dataDeFechamento'])
: null;
Map<String, dynamic> toJson() {
return {
'userId': userId,
'titulo': titulo,
'dataDeAbertura': dataDeAbertura.toIso8601String(),
'descricao': descricao,
'dataDeFechamento': dataDeFechamento?.toIso8601String(),
};
}
}models/chamado.dart
Serialização
-
Serialização é feita "na unha"
-
Dart não suporta reflection em tempo de execução
-
Há geradores de código (json_serializable) para quebrar o galho
-
Hoje é mais tranquilo com IA Generativa
Enviando dados para a API
Future<Chamado> cadastraChamado(Chamado chamado) async {
http.Response resposta = await http.post(
Uri.parse('$_BASE_URL/chamados'),
headers: <String, String>{
'Content-Type': 'application/json',
},
body: jsonEncode(chamado.toJson()),
);
if (resposta.statusCode == 201) {
return Chamado.fromJson(jsonDecode(resposta.body));
} else {
debugPrint('ERRO: ${resposta.body}');
throw Exception('Erro ao criar chamado');
}
}services/api.dart
Método POST
Listagem com API
class _ListaDeChamadosState extends State<ListaDeChamados> {
List<Chamado> _chamados = [];
// Demais métodos...
@override
void initState() {
super.initState();
carregaListaDeChamados();
}
void carregaListaDeChamados() async {
debugPrint('Buscando chamados');
_chamados = await listaChamados();
_chamados.sort(
(a, b) => b.dataDeAbertura.compareTo(a.dataDeAbertura)
);
setState(() {});
}
}screens/suporte/lista_chamados.dart
Inicializando estado
Cadastrando no formulário
// DEMAIS CÓDIGOS OMITIDOS
FilledButton(
onPressed: () {
String id = context.read<UsuarioProvider>().usuario!.id;
criaChamado(id).then(
(chamado) => Navigator.of(context).pop(chamado),
);
},
child: const Text('Enviar chamado'),
)
Future<Chamado> criaChamado(String userId) async {
return cadastraChamado(
Chamado(
userId: userId,
titulo: campoTitulo.text,
dataDeAbertura: DateTime.now(),
descricao: campoDescricao.text,
),
);
}screens/suporte/formulario_novo_chamado.dart
Inicializando estado
Armazenamento local
com SharedPreferences
Shared Preferences
Características
-
Plugin para armazenamento chave-valor (similar ao Storage API do navegador)
-
Dados são salvos no dispositivo do usuário
-
Pequeno volume de dados
-
Consistente am ambas plataformas

Shared Preferences
import 'package:shared_preferences/shared_preferences.dart';
Future<void> registraLogin(Usuario usuario) async {
var prefs = await SharedPreferences.getInstance();
await prefs.setBool('logado', true);
await prefs.setString('usuario', jsonEncode(usuario.toJson()));
}
Future<Usuario?> usuarioLogado() async {
var prefs = await SharedPreferences.getInstance();
var usuarioJson = prefs.getString('usuario');
if (usuarioJson == null) {
return null;
}
return Usuario.fromJson(jsonDecode(usuarioJson));
}
Future<void> limpaLogin() async {
var prefs = await SharedPreferences.getInstance();
await prefs.setBool('logado', false);
await prefs.remove('usuario');
}services/autenticacao.dart
Salvando e lendo dados
Mão-na-massa
- Implementar a listagem de faturas usando o pacote http.
- Use dados fictícios e simples
Desafio

Aula 8
Resiliência em APIs
1
Splash Screen
2
Conceitos elementais de animação
3
Animações
implícitas
4
Transição de
telas customizadas
5
Desafio
Splash Screen


Splash Screen
Nativo vs Flutter
-
Implementada nativamente fora do Flutter
-
Exibida enquanto o Flutter está carregando
-
Só aparece quando necessário
-
Obrigatória para apps no iOS
-
Implementada dentro do framework do Flutter
-
Apresentada depois do Flutter estar carregado
-
Exibida mesmo em warm start (processo do app é trazido para foreground)
Splash Screen Nativa
Tela de introdução Flutter
Native Splash Screen
flutter_native_splash:
android: true
ios: true
web: false
image: assets/images/logo_vivo.jpg
color: "#8533AD"pubspec.yaml
Configuração e geração
dart run flutter_native_splash:createterminal
Animações
Princípios básicos
A
B
Interpolação
(movimento de A para B)
(class Tween)
Princípios básicos
Animações implícitas
O que são?
-
Animação que ocorre quando um atributo do widget muda
-
Basta indicar a duração (e a curva, preferencialmente) que o Flutter controla
-
Fáceis de usar
-
Existem vários prontos (AnimatedXpto) 🥳
-
Usos: expandir/colapsar menus, seleção de elementos, esmaecer, transições suaves entre telas, etc...

AnimatedContainer
/**
* CardChamado virou stateful
*/
class _CardChamadoState extends State<_CardChamado> {
bool selecionado = false;
Icon? icone;
@override
void initState() {
super.initState();
icone = widget._chamado.fechado
? const Icon(Icons.check_circle, color: Colors.green)
: const Icon(Icons.error_outline, color: Colors.grey);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => setState(() => selecionado = !selecionado),
/**
* Trocou Container por AnimatedContainer,
* configurou curva e duração, e pronto!
*/
child: AnimatedContainer(
duration: const Duration(milliseconds: 300),
curve: Curves.fastLinearToSlowEaseIn,
padding: const EdgeInsets.symmetric(vertical: 8),
decoration: BoxDecoration(
color: selecionado
? Colors.grey.shade300
: Colors.white,
border: Border(
bottom: BorderSide(color: Colors.grey.shade300),
),
),
// Demais configurações do container omitidas
),
);
}screens/suporte/lista_chamados.dart

AnimatedOpacity
class _AvisoDeFaturasState extends State<AvisoDeFaturas> {
double opacidade = 1;
bool removerAviso = false;
void removeAviso() {
setState(() => opacidade = 0);
Future.delayed(Duration(milliseconds: 800), () {
setState(() => removerAviso = true);
});
}
@override
Widget build(BuildContext context) {
return Visibility(
visible: !removerAviso,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 800),
curve: Curves.easeOutQuad,
opacity: opacidade,
child: Wrap(
runSpacing: 20,
children: [
Text(
'Você tem prontas para pagar. Quer pagar agora?',
style: TextStyle(fontSize: 28),
),
FilledButton(onPressed: () {}, child: Text('Pagar faturas')),
TextButton(
onPressed: removeAviso,
child: Text('Ocultar notificação'),
),
],
),
),
);
}
}screens/home.dart

AnimatedAlign
Outros exemplos...
AnimatedCrossFade
AnimatedSize
AnimatedList
Hero
...
Transições de tela
Transição de tela
Route rotaDeslizante(Widget tela) {
/**
* Dispensa o MaterialPageRoute (quem fazia a animação
* da transição e construía a página para inserir na tela).
*/
return PageRouteBuilder(
pageBuilder:
(context, animation, secondaryAnimation) => tela,
transitionDuration: const Duration(milliseconds: 500),
transitionsBuilder: (
context, animation, secondaryAnimation, child
) {
const begin = Offset(-1, 0);
const end = Offset.zero;
const curve = Curves.ease;
var tween = Tween(begin: begin, end: end)
.chain(CurveTween(curve: curve));
final offsetAnimation = animation.drive(tween);
return SlideTransition(
position: offsetAnimation,
child: child,
);
},
);
}widgets/util.dart

Sobrescrevendo a transição
Transição de tela
context.read<UsuarioProvider>()
.login(usuario)
.then((_) {
Navigator.of(context)
.pushReplacement(
rotaDeslizante(const Home())
);
});widgets/util.dart

Usando o PageRouteBuilder
- Implementar a seção Especiais pra você com o o pacote carousel_slider.
- Use a mesma imagem e chamada; o foco é a animação. 😉
Desafio

Aula 9
Enterprise Integration Patterns
1
Tipos de teste
no Flutter
2
Escrever
testes
3
Boas (más) práticas
4
Desafio
Quem nunca fez um teste automatizado, levanta a mão. 🤚🏼

Testes de unidade
- Não dependem de recursos externos
- Mais fáceis de escrever e rápidos de executar
Testes no Flutter
Testes de Widgets
- Flutter oferece mecanismos para controlar (e interagir com) widgets
- Mais lentos e difíceis de criar
- Pacote flutter_test
Testes de integração
- Foco em testar o aplicativo como um todo (GUI testing)
- É possível executar no target device e no host machine
- Pacote integration_test
Teste de unidade
Teste de widget
Boas práticas
- Implemente um caso de teste para a Central de faturas que verifique se tem uma fatura aberta, uma atrasada e duas pagas.
Desafio

Aula 9
Event-driven Architecture
É "só" mensageria com outra mentalidade
Revisão a jato
Tipos de mensagem
Semântica faz toda diferença

Domain events
Reagindo a acontecimentos importante
Domain Event:
Uma ocorrência significativa em termos de negócio em um determinado Bounded Context.
PedidoRealizado
FaturaPaga
CancelamentoEfetuado
PagamentoRecusado
Event-driven Architecture
Conceitos-chave
Natureza da comunicação

1
Desafios
2
Considerações
sobre interface
3
Interação com
código nativo
4
Estado atual das
novas plataformas
Vantagens e desafios
Vantagens
Alcance de Público Amplo
Consistência de Experiência do Usuário
Economia de Tempo e Recursos
Desafios
Compatibilidade e Adaptação
Limitações de Desempenho
Limitações de API nativa
Considerações
Adaptação de Interface: Consistência entre iOS, Android e Web
Usar elementos específicos da plataforma
Experiência direcionada




Cupertino
Material
Padronização + interação + feedback
Consistência de design
-
Diretrizes de design
-
disposição de elementos, navegação, gestos, etc
-
-
Espaçamento e alinhamento
-
+ que simples estética → usabilidade e harmonia
-
SizedBox e Padding
-
-
Testes de usabilidade
-
Feedback de usuários (insights de melhoria, etc)
-
Gerenciamento de
Dependências
Method Channel
Dependências nativas
- Habilita aplicativo interagir diretamente com código nativo
- Possibilita instalar e usar dependências nativas

1 - Dependência da plataforma
Method channel - Etapas
// build.gradle
dependencies {
implementation 'com.example:your-library:1.0.0'
}
# Podfile
pod 'YourLibrary', '~> 1.0'
2 - Código nativo
Method channel - Etapas
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "com.example/native";
@Override
public void configureFlutterEngine(FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
new MethodChannel(
flutterEngine.getDartExecutor().getBinaryMessenger(),
CHANNEL
)
.setMethodCallHandler(
(call, result) -> {
if (call.method.equals("getData")) {
// Chame sua biblioteca aqui
String data = getNativeData();
result.success(data);
} else {
result.notImplemented();
}
}
);
}
}
2 - Código nativo
Method channel - Etapas
import UIKit
import Flutter
@UIApplicationMain
class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions:
[UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
let controller = window?.rootViewController as! FlutterViewController
let channel = FlutterMethodChannel(name: "com.example/native",
binaryMessenger: controller.binaryMessenger)
channel.setMethodCallHandler { (call, result) in
if call.method == "getData" {
let data = self.getNativeData() // Chame sua biblioteca aqui
result(data)
} else {
result(FlutterMethodNotImplemented)
}
}
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
3 - Ponte de comunicação
Method channel - Etapas
import 'package:flutter/services.dart';
class NativeBridge {
static const MethodChannel _channel = MethodChannel('com.example/native');
Future<String> getNativeData() async {
final String data = await _channel.invokeMethod('getData');
return data;
}
}
void fetchData() async {
String data = await NativeBridge().getNativeData();
print(data);
}
Considerações
Method Channels
-
Por quê?
-
Implementar algo que não tem no Flutter
-
Acessar recursos específicos da plataforma (sensor, câmera, etc)
-
Reusar dependência nativa
-
-
Cuidados
-
Compatibilidade e Manutenção 🏋🏼
-
Web e Desktop com Flutter
Panorama
Web e Desktop
-
-
Versão Beta
-
Responsividade, PWA e canvas (algumas renderizações)
-
Desempenho, SEO e tamanho do bundle final
-
-
-
Versão Alpha
-
Renderização de alto desempenho, mais possibilidade de periféricos, etc
-
Estabilidade, recursos ainda indisponíveis, documentação
-





Obrigado
(def pessoa {:nome "Cácio José da Costa Silva"
:linkedin "/cacio-costa/"
:email "cacio.costa@alura.com.br"
:repo "https://github.com/cacio-costa/vivoapp"
:ig-grupo-qdqa "@grupoqdqa"})Aula 11
Mega-revisão
O que é o React

-
Biblioteca open source para a construção de UI
-
Baseada em componentes
-
Permite criar aplicações em grande escala, com o conceito de SPA
-
Criado pelo Facebook em 2013
Funcionamento dos websites
Server Side Rendering

Funcionamento dos websites
Client Side Rendering


Animação de loading no site da Coca-Cola
Por que usar React
| Componentização | Virtual DOM |
| Fluxo de dados unidirecional | JSX (JavaScript XML) |
| Ecossistema de bibliotecas | React Native |
| SSR com Next.js | React DevTools |
| Atualizações frequentes | Comunidade de suporte |

Fundamentos
Fluxo de dados unidirecional
Virtual DOM
JSX
Ecossistema React
Conceitos
Props
State
Renderização
condicional
Componentes
Recursos
Axios
Testes
Memoization
CSS-in-JS
Gerenciamento de estado

<input type="text" placeholder="Pesquisar" />
<h1>Título Principal</h1>
<a href="https://www.fiap.com.br/" target="_blank">
FIAP
</a>
<img src="https://www.alura.com.br/assets/img/alura2023/home/formations-sub.1712930780.png" alt="Ilustração de um submarino parado no fundo do mar. A frente do submarino possui uma escotilha esférica grande. Ele é ladeado por outras 4 escotilhas menores. O submarino tem características futuristas. O submarino é iluminado por uma luz azulada que vem de cima. Atrás do submarino, em segundo plano, surgem de cima duas algas extensas e verticais. O fundo do mar está populado por corais e mais vegetação maritima.">O que preciso saber?



HTML
css
const itemsMenu = [
{
item: 'Baixe o App Vivo',
link: 'https://vivo.com.br/para-voce/app-vivo'
},
{
item: 'Produtos e Serviços',
link: 'https://vivo.com.br/para-voce/produtos-e-servicos'
},
{
item: 'Ajuda',
link: 'https://vivo.com.br/para-voce/ajuda'
},
{
item: 'Por que Vivo',
link: 'https://vivo.com.br/para-voce/por-que-vivo'
},
{
item: 'Melhores Ofertas',
link: 'https://vivo.com.br/para-voce/produtos-e-servicos/melhores-ofertas'
}
]O que preciso saber?
JavaScript

-
Arrays e objetos
-
Métodos para manipulação de arrays
-
Funções
Por quê?

function MenuList() {
return (
<nav>
<ul>
{itemsMenu.map((menuItem, index) => (
<li key={index}>
<a href={menuItem.link}>{menuItem.item}</a>
</li>
))}
</ul>
</nav>
)
}A base do React moderno são componentes funcionais, com a utilização de métodos nativos do JS.
Ferramentas necessárias




Navegador
Node.js
VS Code

Ambiente de desenvolvimento
Build e transpilers
- Webpack
- Babel
3.
Servidor
- create-react-app
- vite
1.
Gerenciador de pacotes
- npm
- yarn
2.

Airbnb

Empresas que utilizam





Empresas que utilizam

{concorrentes}



export function Saudacao(props) {
return <h1>Olá, {props.nome}!</h1>;
}
function App() {
return (
<div>
<Header />
<Saudacao nome="Fulana" />
</div>
)
}
export default App
Componentes
-
Blocos de construção de interfaces
-
Podem ser funcionais ou de classe
-
Encapsulam lógica e apresentação
Criar componente reutilizável para a seção "Por que escolher a Vivo?"
Desafio


Criando uma aplicação React com Vite (artigo)
Nossas aulas
null
Anterior
Próxima
Aula 1
24/06 às 15h
→ Introdução ao React
Atual
Aula 2
25/06 às 14h
→ JXS e Elementos React
JSX e Elementos React

Aula 2
1
O que é o JSX
2
Renderização de elementos
3
Componentes
5
Eventos
4
Props
6
Desafio
O que é o JSX
-
HTML com JavaScript
-
Facilita a criação de componentes
-
Convertido em chamadas
React.createElement
function Botao(props) {
return (
<button onClick={props.onClick}>
{props.texto}
</button>
)
}function Input(props) {
return (
<input
value={props.value}
onChange={props.onChange}
placeholder={props.placeholder}
/>
)
}function Lista(props) {
return (
<ul>
{props.itens.map(item => (
<li key={item.id}>{item.nome}</li>
))}
</ul>
)
}Renderização de elementos
-
Elementos são blocos básicos do React
-
Renderiza elementos no DOM
-
Virtual DOM
-
ReactDOM.render()

function App() {
const [texto, setTexto] = React.useState('')
const [itens, setItens] = React.useState([])
const adicionarItem = () => {
const novoItem = { id: itens.length + 1, nome: texto }
setItens([...itens, novoItem])
setTexto('')
}
return (
<div>
<h1>Minha Lista</h1>
<Input
value={texto}
onChange={e => setTexto(e.target.value)}
placeholder="Digite um item"
/>
<Botao onClick={adicionarItem} texto="Adicionar" />
<Lista itens={itens} />
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
)Renderização de elementos
-
Blocos de construção
-
São reutilizáveis
-
Encapsulam lógica e estilo
-
Podem ser funcionais ou de classe
Componentes
function Botao(props) {
const handleClick = () => {
props.onClick()
}
return (
<button className={`btn ${props.className}`} onClick={handleClick}>
{props.texto}
</button>
)
}

Props
-
Passam dados de um componente pai para um componente filho
-
São de leitura única (read-only)
-
Permitem a reutilização de componentes

"Olá, mundo!"
Pai
Props

// Componente Filho
function Filho(props) {
return <p>{props.mensagem}</p>
}
// Componente Pai
function Pai() {
const mensagemParaFilho = "Olá, Mundo!"
return <Filho mensagem={mensagemParaFilho} />
}
// O componente Post
function Post(props) {
return (
<div>
<h1>{props.titulo}</h1>
<img loading="lazy" src={props.urlImagem} />
<p>{props.texto}</p>
</div>
)
}
// Usando o componente Post
function App() {
return (
<div>
<Post
titulo="Primeiro post"
urlImagem="https://example.com/image1.jpg"
texto="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
<Post
titulo="Segundo post"
urlImagem="https://example.com/image2.jpg"
texto="Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
/>
</div>
)
}Eventos
-
Sintaxe similar ao HTML
-
Funções de evento como props
-
Usados para dinamismo e interatividade
Ações que acontecem no navegador

Eventos

function Botao(props) {
return (
<button onClick={props.onClick}>
Clique aqui
</button>
)
}
function App() {
const handleClick = () => {
alert('Botão clicado!')
}
return (
<div>
<h1>Evento de Clique</h1>
<Botao onClick={handleClick} />
</div>
)
}
ReactDOM.createRoot(document.getElementById('root')).render(
<App />
)
Criar um componente para Botão que possa ser reutilizado em toda a página.
Desafio



Nossas aulas
Anterior
Próxima
Aula 3
26/06 às 10h
Componentes de Classe vs. Componentes Funcionais

Atual
Aula 2
JSX e Elementos React
Aula 1
Introdução ao React
Aula 3
Componentes de Classe vs. Componentes Funcionais

1
Class components
2
Estado e ciclo de vida
3
Function components
4
Hooks em componentes funcionais
6
Desafio
Formato dos componentes

Componente Funcional
function Saudacao() {
return <h1>Olá, mundo!</h1>
}Componente de Classe
import React, { Component } from 'React'
class Saudacao extends React.Component {
render() {
return <h1>Olá, mundo!</h1>
}
}-
Definidos como classes ES6
-
Estendem
React.Component -
Podem ter estado (
state) -
Possuem métodos de ciclo de vida
-
Devem implementar o método
render
Class Components
class Welcome extends React.Component {
render() {
return <h1>Hello, Vivo!</h1>
}
}
Class Components
import React, { Component } from 'react'
export class Input extends Component {
handleChange = (event) => {
this.props.onChange(event)
}
render() {
return (
<input
className={`input ${this.props.className}`}
value={this.props.value}
onChange={this.handleChange}
placeholder={this.props.placeholder}
/>
)
}
}
→ Estado e Ciclo de Vida é mantido em this.state
→ Métodos como:
componentDidMountcomponentDidUpdatecomponentWillUnmount
Class Components

→ Itens de Class e OO, além da classe
- Constructor
- Herança
- Instância
- Encapsulamento
- Ciclo de Vida
Estado em class components

No constructor
Armazena dados mutáveis
Definição
Usa this.setState()
Aceita objeto ou função
Atualização
Mudanças no estado re-renderizam o componente
Reactividade

import React, { Component } from 'react'
class Contador extends Component {
constructor(props) {
super(props);
this.state = {
contagem: 0
}
}
incrementar = () => {
this.setState((prevState) => ({
contagem: prevState.contagem + 1
}))
}
render() {
return (
<div>
<h1>Contagem: {this.state.contagem}</h1>
<button onClick={this.incrementar}>Incrementar</button>
</div>
)
}
}
export default Contador
props
setState()
forceUpdate()
Montagem
Atualização
Desmontagem
Fases do ciclo de vida
-
Baseados em funções JavaScript
-
Retornam JSX
-
Simples e concisos
-
Recebem props como argumento
-
Originalmente sem estado (
state)-
Por isso não podíamos usar antes, mas os hooks resolveram isso 👍
-
Function
Components
function OlaMundo() {
return <h1>Olá, mundo!</h1>
}
function Botao(props) {
return (
<button onClick={props.onClick}>
{props.texto}
</button>
)
}
export default Botao

Vantagens e desvantagens
Suporte a ciclo de vida
Class
Mais verbosos
Ciclo de vida mais complexo
Function
Mais simples, menos código
Hooks para estado e efeitos
Sem estado antes dos Hooks


Hooks em Componentes Funcionais
-
Introduzidos no React 16.8
-
Adicionam estados
-
Atualizar dados ao longo de seu ciclo de vida
-
-
Controlam efeitos colaterais
-
Algo que está fora do escopo do componente
-

import { useState } from 'react'
export function Contador() {
const [contagem, setContagem] = useState(0)
const incrementar = () => {
setContagem(contagem + 1)
}
return (
<div>
<h1>Contagem: {contagem}</h1>
<button onClick={incrementar}>Incrementar</button>
</div>
)
}
useState
import { useState, useEffect } from 'react'
export function Relogio() {
const [hora, setHora] = useState(new Date())
useEffect(() => {
const intervalo = setInterval(() => {
setHora(new Date())
}, 1000)
return () => clearInterval(intervalo)
}, [])
return (
<div>
<h1>{hora.toLocaleTimeString()}</h1>
</div>
)
}
useEffect
Crie todos os componentes que faltam na nossa página, para podermos evoluir nosso aprendizado com o uso de states e hooks personalizados.
Desafio


Nossas aulas
Anterior
Próxima
Aula 4
26/06 às 15h
Estado e Gerenciamento de Estado

Atual
Aula 3
Componentes de Classe vs. Componentes Funcionais
Aula 2
JSX e Elementos React
Aula 4
Estado e Gerenciamento de Estado

1
useState e useEffect
2
O que é o Gerenciamento de Estado?
5
useContext
4
6
Desafio
Context API
Flux vs. Redux
3
useState

-
Hook do React para estados (a partir do React 16.8)
-
Podemos controlar / manusear o estado de um componente
-
Atribuído a eventos em funções de mudança de
state -
Inicializa estado
-
Atualiza com
setState
useState

import { useState } from 'react'
export function Contador() {
const [contador, setContador] = useState(0)
const incrementar = () => {
setContador(contador + 1)
}
const decrementar = () => {
setContador(contador - 1)
}
const resetar = () => {
setContador(0)
}
return (
<div>
<h1>Contador: {contador}</h1>
<button onClick={incrementar}>Incrementar</button>
<button onClick={decrementar}>Decrementar</button>
<button onClick={resetar}>Resetar</button>
</div>
)
}
useEffect

-
Hook para efeitos colaterais (a partir do React 16.8)
-
Podemos averiguar esses efeitos:
-
a cada renderização do componente
-
em alguma alteração do estado do componente
-
apenas na primeira vez que o componente for renderizado
-
* Quando formos fazer requisições em API's, mas veremos isso na aula sobre AXIOS!
useEffect

import { useState } from 'react'
export function Contador() {
const [contador, setContador] = useState(0)
const incrementar = () => {
setContador(contador + 1)
}
const decrementar = () => {
setContador(contador - 1)
}
const resetar = () => {
setContador(0)
}
useEffect(() => {
console.log(`O contador foi atualizado para: ${contador}`);
}, [])
return (
<div>
<h1>Contador: {contador}</h1>
<button onClick={incrementar}>Incrementar</button>
<button onClick={decrementar}>Decrementar</button>
<button onClick={resetar}>Resetar</button>
</div>
)
}
O que é gerenciamento de Estado?
- Gerenciar o estado com muitas informações que podem mudar ao longo do tempo e afetar a renderização do componente
- Facilita a manutenção e previsibilidade do comportamento do aplicativo, assegurando que a interface do usuário esteja sempre sincronizada com os dados
Tipos de Estado

-
Estado Local: Gerenciado dentro de um único componente usando
useStateouthis.state
-
Estado Global: Compartilhado entre múltiplos componentes (Context API, Redux etc.)
Flux vs. Redux
Centraliza o estado da aplicação em um único lugar global. Isso simplifica o acesso e a atualização do estado, evitando a necessidade de passar props através de múltiplos componentes.
Redux
Facilita a manutenção e escalabilidade em aplicações grandes, especialmente quando várias partes da aplicação precisam estar sincronizadas e atualizadas conforme o estado muda
Flux

Context API


Prop Drilling
import { createContext, useContext, useState, useEffect } from 'react'
export const useUser = () => useContext(UserContext)
export const UserContext = createContext()
export function UserProvider ({ children }) {
const [user, setUser] = useState({})
useEffect(() => {
setUser({
isLogged: true,
firstName: 'Milena',
lastName: 'Emmert',
age: 28
})
}, [])
return <UserContext.Provider value={user}>{children}</UserContext.Provider>
}
ReactDOM.createRoot(document.getElementById('root')).render(
<UserProvider>
<App />
</UserProvider>
)import { useUser } from '../contexts'
export function Componente() {
const user = useUser()
return (
<div>{user.firstName}</div>
)
}Fazer o Header do nossa página com o controle de login true ou false.
Desafio

Nossas aulas
Anterior
Próxima
Aula 5
28/06 às 10h
Roteamento com React Router

Atual
Aula 4
Estado e Gerenciamento de Estado
Aula 3
Componentes de Classe vs. Componentes Funcionais
Aula 5
Roteamento com React Router

1
O que são Rotas
2
O que é a React Router
4
Navegação e parâmetros
6
Desafio
Proteção de Rotas
5
Configuração inicial
3




O que são rotas
Organização e navegação das páginas, dentro do contexto de SPA


{Função das rotas nas SPAs}
-
Permite a navegação sem recarregar a página
-
Mantém o estado da aplicação
-
-
Em rotas CSR o gerenciamento das rotas ocorre no navegador
O que é a React Router
-
Biblioteca de roteamento para React
-
Facilita a criação de rotas dinâmicas
-
Suporte para rotas aninhadas e parâmetros
-
Podemos ter navegação fluida

Configuração
A React Router Dom não é uma lib nativa, portanto precisamos importá-la.
Importar

npm install react-router-dom

Adicionando uma Rota
Configuração da nossa primeira rota
→ Precisamos envelopar an app com Browser Router
-
Context de roteamento do React Router
-
Gerencia a URL da aplicação com a navegação entre componentes
Browser Router
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { App } from './app'
ReactDOM.createRoot(document.getElementById('root')).render(
<BrowserRouter>
<App />
</BrowserRouter>
)

Navegação
Pode substituir a tag <a> ou pode ser usado para navegação interna
NavLink
Quando a navegação precisa ser ativada programaticamente, como em eventos ou lógica interna do componente
Navigate ou useNavigate
import { NavLink } from 'react-router-dom';
<Link to="/about">About</Link>import { useNavigate } from 'react-router-dom'
function MyComponent() {
const navigate = useNavigate()
function handleClick() {
navigate('/about')
}
return (
<button onClick={handleClick}>Go to About</button>
)
}
Route
Rotas individuais
Route e Routes
import { Route } from 'react-router-dom'
<Route path="/about" component={About} />Routes
Agrupa várias rotas em um único componente
import { Routes, Route } from 'react-router-dom'
<Routes>
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
Route e Routes
<Routes>
<Route path='/' element={<MainScreen />} />
<Route path='/planos' element={<InternetScreen />} />
<Route path='/duvidas' element={<FaqScreen />} />
</Routes>

Proteção das Rotas
const Auth = ({ children, condition }) => {
if (condition) {
return children
} else {
return <Navigate to='/' />
}
}<Route
path='/perfil'
element={
<Auth condition={isLogged}>
<ProfileScreen />
</Auth>
}
/>Verificar autenticação antes de renderizar componentes
Criar componente que protege a rota
Criar outras telas (apenas de teste) na nossa app para fazer o uso das rotas.
Desafio


Nossas aulas
Anterior
Próxima
Aula 6
02/07 às 14h
Formulários e Eventos em React

Atual
Aula 5
Roteamento com React Router
Aula 4
Estado e Gerenciamento de Estado
Aula 6
Formulários e Eventos em React

1
Formulários com React
2
Formik
3
Validação de formulários
5
Eventos
4
Yup
6
Desafio
Formulários com React




Formulários com React

-
Usamos elementos HTML para coletar dados do usuário
-
<input> -
<textarea> -
<select>
-
-
Podemos reutilizar elementos trocando os valores de atributos por meio das props
-
Types para inputs. Alguns exemplos: text, email, number
-
Types para botões. Alguns exemplos: submit, button
-
-
Criamos de funções de manipulação
<form onSubmit={handleSubmit}>
<div>
<label>Nome:</label>
<input
type="text"
value={nome}
onChange={handleNomeChange}
required
/>
</div>
<div>
<label>Profissão:</label>
<select value={profissao} onChange={handleProfissaoChange} required>
<option value="">Selecione uma profissão</option>
<option value="Desenvolvedor">Desenvolvedor</option>
<option value="Designer">Designer</option>
<option value="Engenheiro">Engenheiro</option>
<option value="Professor">Professor</option>
</select>
</div>
<button type="submit">OK</button>
</form>const [nome, setNome] = useState('')
const [profissao, setProfissao] = useState('')
const handleNomeChange = (event) => {
setNome(event.target.value)
}
const handleProfissaoChange = (event) => {
setProfissao(event.target.value)
}
const handleSubmit = (event) => {
event.preventDefault()
console.log('Nome:', nome)
console.log('Profissão:', profissao)
}const [nome, setNome] = useState('')
const [profissao, setProfissao] = useState('')
function handleNomeChange(event) {
setNome(event.target.value)
}
function handleProfissaoChange(event) {
setProfissao(event.target.value)
}
function handleSubmit(event) {
event.preventDefault()
console.log('Nome:', nome)
console.log('Profissão:', profissao)
}<input /> nos Formulários
{onChange}
→ Gerenciamento do estado dos campos de entrada
-
Manipular e validar os dados de entrada
-
Manter a UI sincronizada com o estado do aplicativo

Formik
- Biblioteca para gerenciamento de formulários
- Gerencia
- Estado
- Validação
- Envio



Formik

import { Formik, Field, Form } from 'formik'
const Componente = () => {
retun (
<div>
<h1>Sign Up</h1>
<Formik
initialValues={{
firstName: '',
lastName: '',
email: '',
}}
onSubmit={(values) => console.log(values)}
>
<Form>
<label htmlFor="firstName">First Name</label>
<Field id="firstName" name="firstName" placeholder="Jane" />
<label htmlFor="lastName">Last Name</label>
<Field id="lastName" name="lastName" placeholder="Doe" />
<label htmlFor="email">Email</label>
<Field
id="email"
name="email"
placeholder="jane@acme.com"
type="email"
/>
<button type="submit">Submit</button>
</Form>
</Formik>
</div>
)
}Formik



- Biblioteca para validação
- Define e valida formatos de dados
- Integração com Formik para validação de formulários
Yup
const formValidationSchema = Yup.object({
name: Yup.string().required('O nome é obrigatório.'),
email: Yup.string()
.email('Formato de e-mail inválido.')
.required('O e-mail é obrigatório.'),
age: Yup.number()
.typeError('A idade deve conter apenas números.')
.integer('A idade deve ser um número inteiro.')
.min(1, 'Idade inválida.')
.max(150, 'Idade inválida.'),
cep: Yup.string()
.matches(/^\d{5}-\d{3}$/, 'O CEP deve possuir o formato 00000-000.')
.required('O CEP é obrigatório.'),
message: Yup.string().required('A mensagem é obrigatória.')
})Yup

Implementar o nosso formulário com as validações de erro e reutilizar nosso componente de Botão já criado.
Desafio

Nossas aulas
Anterior
Próxima
Aula 7
03/07 às 10h
Chamadas de API com Axios

Atual
Aula 6
Formulários e Eventos
Aula 5
Roteamento com React Router
Aula 7
Chamadas de API com Axios

1
Axios
2
GET
3
POST
5
Segurança de API's
4
Tratamentos de erros e async/await
6
Desafio
-
Faz requisições HTTP (transferência de dados na web)
-
Baseada em
promisespara lidar com requisições assíncronas -
Simplifica comunicação com APIs
-
Tranformações
-
Cancelamentos
-
-
Transforma os dados para JSON


npm install axiosRequisições HTTP
Forma como os dados são transferidos na web


GET
Recupera dados do servidor
import axios from 'axios'
axios.get('/api/users')
.then(response => console.log(response.data))
.catch(error => console.error(error))

POST
Envia dados ao servidor
axios.post('/api/users', { name: 'John' })
.then(response => console.log(response.data))
.catch(error => console.error(error))
import axios from 'axios'
axios.get('/api/users')
.then(response => console.log(response.data))
.catch(error => {
if (error.response) {
console.error('Error ', error.response.status)
} else {
console.error('Error:', error.message)
}
})Possíveis erros

Async/Await
- Sintaxe assíncrona para requisições
- Usa
asynceawait
import axios from 'axios'
async function fetchData() {
try {
const response = await axios.get('/api/users')
console.log(response.data)
} catch (error) {
console.error(error)
}
}async function fetchData() {
try {
const response = await fetch('/api/users')
const data = await response.json()
console.log(data)
} catch (error) {
console.error(error)
}
}

Segurança de APIs
-
Token para autenticação segura
-
Incluído em headers das requisições
import axios from 'axios'
const token = 'your-token'
axios.get('/api/secure-data', {
headers: {
Authorization: `${token}`
}
})
.then(response => console.log(response.data))
.catch(error => console.error(error))

Fazer o get da api ViaCep a partir do CEP inserido no campo do formulário.
Desafio


Nossas aulas
Anterior
Próxima
Aula 8
03/07 às 15h
Estilização em React

Atual
Aula 7
Chamadas de API com Axios
Aula 6
Formulários e Eventos
Anterior
Próxima
Aula 8
03/07 às 15h
Estilização em React

Atual
Aula 8
Estilização com React

1
CSS em React
2
Styled Components
3
Imagens no React
5
CSS modules
4
SVG's no React
6
Desafio
CSS em React
-
Estiliza os componentes e interfaces
-
Métodos diversos
-
CSS tradicional
-
Bibliotecas (Styled Components 💅)
-
Pré-processadores (para mixins, nesting e funções)
-
CSS Modules*
-


Styled Components
- Biblioteca para CSS-in-JS
- Escreve estilos com JavaScript
- Estilos escopados por componente
import styled from 'styled-components';
const Button = styled.button`
background: blue;
color: white;
padding: 10px;
`;
function App() {
return <Button>Clique aqui</Button>;
}npm install styled-components
const Button = styled.button<{ $primary?: boolean; }>`
background: transparent;
border-radius: 3px;
border: 2px solid #BF4F74;
color: #BF4F74;
margin: 0.5em 1em;
padding: 0.25em 1em;
${props => props.$primary && css`
background: #BF4F74;
color: white;
`}
`;
const Container = styled.div`
text-align: center;
`
render(
<Container>
<Button>Normal Button</Button>
<Button $primary>Primary Button</Button>
</Container>
);

Imagens no React
- Utilizamos o
import
import whyUsQuality from '../assets/images/why-us-quality.png'
import whyUsServices from '../assets/images/why-us-services.png'
import whyUsSignal from '../assets/images/why-us-signal.png'

SVG's no React
- Função que retorna elemento SVG num JSX
- Importe os componentes SVG onde necessário
- Utilize-os como componentes JSX normais
SVG
export const arrow = (className) => {
return (
<svg
className={className}
viewBox='0 0 32 32'
fill='none'
xmlns='http://www.w3.org/2000/svg'
>
<path
d='M18 6L28 16M28 16L18 26M28 16H4'
stroke='currentColor'
strokeWidth='2'
strokeLinecap='round'
strokeLinejoin='round'
vectorEffect='non-scaling-stroke'
/>
</svg>
)
}

import styles from './Button.module.css';
function App() {
return <button className={styles.button}>Clique aqui</button>;
}CSS Modules
- Escopo local por padrão.
- Estilos importados como módulos
- Evita conflitos de nome de classes

CSS Modules

O css-modules cria um className único para cada local em que é utilizado


CSS Modules e as classes globais
.faqScreen {
composes: screen from global;
background-color: var(--rose);
padding-top: 160px;
color: var(--white);
}
.container {
composes: container from global;
}.container {
max-width: var(--container-width);
width: 100%;
height: 100%;
margin: 0 auto;
padding: 0 var(--container-padding);
}

Classes dinâmicas
<div className={isSelected ? ${s.component} ${s.selected} : s.component}></div>Podemos adicionar classes por meio de estruturas condicionais.
Usamos a estrutura de operador ternário:
condition ? expressionIfTrue : expressionIfFalseNossas aulas
Anterior
Próxima
Aula 9
04/07 às 10h
Hooks Avançados

Atual
Aula 8
Estilização em React
Aula7
Chamadas de API com Axios
Aula 9
Hooks Avançados

1
Hooks avançados
2
useMemo
3
Performance
5
Boas práticas
4
Hooks customizados
6
Desafio
Hooks Avançados
-
Melhoram funcionalidade dos componentes funcionais
-
Simplificam lógica complexa
-
Promovem reutilização de código

useMemo
-
Memoriza valores calculados
-
Evita cálculos desnecessários
-
Otimiza o desempenho


useMemo

const ShoppingCart = ({ items }) => {
const totalPrice = items.reduce((sum, item) => sum + item.price, 0)
return <div>Total Price: {totalPrice}</div>
}import { useMemo } from 'react'
const ShoppingCart = ({ items }) => {
const totalPrice = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0)
}, [items])
return <div>Total Price: {totalPrice}</div>
}Performance
useMemomemoização de dadosusar gerenciamento de estado de forma eficientelazy loading

hooks customizados

-
Reutilizam lógica entre componentes
-
Simplificam o código
-
Promovem a manutenção e legibilidade
-
Permitem personalizações

Boas práticas

-
Componentização
-
Nomeação consciente
-
Gerenciamento de estado
-
Performance
-
Padrões de codificação
Criar um hook personalizado para que o Vivinho apareça apenas quando nossa MainScreen estiver no topo da tela.
Desafio

Nossas aulas
Anterior
Próxima
Aula 10
05/07 às 10h
Introdução ao TypeScript

Atual
Aula 9
Hooks Avançados
Aula 8
Estilização em React
Aula 10
Introdução ao TypeScript com React

1
O que é o TypeScript?
2
Por que TypeScript com React?
5
Componentes funcionais com TypeScript
4
Tipos básicos e Types
6
Instruções
Configurando o ambiente de desenvolvimento
3
O que é TS?
-
Superset de JavaScript
-
Adiciona tipagem estática
-
Melhora autocomplete e refatoração
-
Detecta erros em tempo de desenvolvimento
-
Compila para JavaScript puro

Por que TS?
TypeScript

-
Websites simples
-
Sem grande volumes de dados
-
Projetos individuais
-
Aplicações mais complexas
-
SPAs
-
Projetos grandes
-
Projetos em equipe
JavaScript x TypeScript
| Característica do sistema de tipos | JS | TS |
|---|---|---|
| Como os tipos são vinculados? | Dinamicamente | Estaticamente |
| Os tipos são convertidos automaticamente? | Sim | Em alguns casos sim |
| Quando os tipos são verificados? | Em tempo de execução | Em dev e em tempo de compilação |
Por que React com TS?
-
Aumento da produtividade no desenvolvimento
-
Redução de bugs através da tipagem estática
-
Facilidade na manutenção de código em projetos grandes
-
Melhor suporte para trabalho em equipe



Configurando o Ambiente de Desenvolvimento



Tipos básicos no TS
| String | let myString: string = "Eu sou uma string" |
| Number | let myNumber: number = 12 |
| Boolean | let myBoolean: boolean = true |
| Array | let myArray: string[] = ["Olá", "Mundo"] |
| Object | let myObject: {name: string, age: number} = {name: "Milena", age: 28} |
| Any | let myAny: any |
Types
-
Tipo personalizável
function Botao({ type, text, isDisabled }) {
return (
<button type={type} disabled={isDisabled}>
{text}
</button>
)
}type BotaoProps = {
type: 'submit' | 'button' | 'reset'
text: string
isDisabled: boolean
}
function Botao({ type, text, isDisabled }: BotaoProps) {
return (
<button type={type} disabled={isDisabled}>
{text}
</button>
)
}
Componentes funcionais com TS
function Apresentacao({ name }: { name: string }) {
return <h1>Hello, {name}!</h1>
}- Para fazer o projeto da nossa LP, acesse o Figma disponibilidado nesse slide.
- Todos os nossos encontros (exceto o encontro 8), foram propostos desafios de acordo com o nível do conteúdo apresentado. Esses desafios estão no final de cada aula, nesses slides.
- Nosso projeto final também pode ser acessado no GitHub, deixei o código comentado com as explicações referentes aos desafios.
Qualquer dúvida você pode me procurar o linkedin.
Será um prazer te ajudar!
Instruções
Materiais gratuitos para consulta
- O que é React JS? #HipstersPontoTube (youtube.com)
- O que saber antes de começar a aprender react.js? (youtube.com)
- Guia do iniciante em React - Hipsters #209
- React: o que é, como funciona e um Guia da biblioteca JS
- React Hooks: o que é e como funcionam?
- Estado do ecossistema React 2023 - Hipsters Ponto Tech #354
- Componentes com Styled Components no React JS
- Prop Drilling: o que é?
- React e Context API: testes de unidade
- Requisições HTTP utilizando o AXIOS
- Boas práticas ao escrever código em React js
- GitHub - braziljs/eloquente-javascript: Tradução do livro Eloquent JavaScript - 2ª edição.
Nossas aulas chegaram ao fim 😢
Anterior
❤️
Te desejo muito sucesso e que você sempre tenha força para superar seus desafios!
Um grande abraço,
Milena Emmert.

Atual
Aula 10
Introdução o TypeScript com React
Aula 9
Hooks Avançados
Jornada APIs e Integração de Sistemas
By cacio-costa
Jornada APIs e Integração de Sistemas
- 2


