📚 Pré-requisitos Teóricos: este projeto consome os serviços da Parte 01 — Spring Boot API e a infraestrutura conteinerizada da Parte 05 — Docker + PostgreSQL.
Seja muito bem-vindo à Parte 07 da série de projetos Lista de Tarefas!
Nesta etapa prática, você aprenderá a dominar a segunda grande trilha de equivalência entre desenvolvimento Web e Mobile Híbrido / Cross-platform:
\[\text{Web SPA (React 19 + Vite)} \longrightarrow \text{Mobile Cross-Platform (React Native + Expo)}\]💡 “Construa interfaces com componentes e deixe o ecossistema complementar.”
Enquanto na Web tradicional montamos interfaces manipulando elementos HTML (<div>, <button>, <input>) renderizados através da árvore do Virtual DOM nos navegadores, no ecossistema mobile com React Native utilizamos o mesmo modelo mental de componentes e estados (useState, useEffect), porém renderizando componentes nativos reais de Android e iOS (<View>, <Text>, <TouchableOpacity>, <FlatList>) através do motor de layout Yoga Flexbox e empacotados com a produtividade do Expo.
Tanto a aplicação Web SPA em React quanto o cliente móvel em React Native operam como consumidores diretos da nossa Fonte Única da Verdade (SSOT): a API REST desenvolvida em Spring Boot na porta 8080, padronizada sob o contrato canônico { id, descricao, concluida }.
useState, useCallback, useFocusEffect) e fluxo de dados unidirecional compartilhados entre Web e Mobile.Um dos aspectos cruciais do cliente móvel é a resolução dinâmica de rede para alcançar a API na máquina de desenvolvimento:
http://localhost:8080/api/tarefas.localhost representa o próprio celular emulado. Para alcançar a API que roda no computador, o serviço utiliza o alias de rede:
\(\text{URL no Emulador Android} = \mathbf{http://10.0.2.2:8080/api/tarefas}\)http://192.168.1.50:8080/api/tarefas).| ID | Descrição | Endpoint REST |
|---|---|---|
| RF-R01 | Consultar e exibir todas as tarefas cadastradas com suporte a pull-to-refresh (RefreshControl). |
GET /api/tarefas |
| RF-R02 | Cadastrar nova tarefa com descrição preenchida e status inicial pendente. | POST /api/tarefas |
| RF-R03 | Alternar o estado de conclusão (concluida = true/false) de forma atômica. |
PATCH /api/tarefas/{id}/status |
| RF-R04 | Modificar a descrição de uma tarefa existente com pré-carregamento dos dados. | PUT /api/tarefas/{id} |
| RF-R05 | Deletar permanentemente uma tarefa após confirmação do usuário. | DELETE /api/tarefas/{id} |
| ID | Categoria | Descrição |
|---|---|---|
| RNF-R01 | Performance de Lista | O cliente mobile deve utilizar FlatList com reciclagem de células de memória para suportar listas volumosas. |
| RNF-R02 | Resolução Dinâmica | O módulo de rede (api.js) deve chavear automaticamente para 10.0.2.2:8080 caso detecte ambiente Android. |
| RNF-R03 | Feedback Tátil & Diálogos | Operações destrutivas devem utilizar alertas nativos do sistema (Alert.alert no mobile e window.confirm na web). |
| RNF-R04 | Ergonomia Móvel | A tela principal deve conter um Floating Action Button (FAB) com elevação e posicionamento ergonômico no canto inferior direito. |
| RNF-R05 | Contrato Canônico | Toda a comunicação com a API deve trafegar exclusivamente o payload { id, descricao, concluida }. |
flowchart LR
User(("👤 Usuário Web / Mobile"))
subgraph "Aplicação React / React Native"
UC01["UC01: Listar Tarefas em FlatList"]
UC02["UC02: Inserir Nova Tarefa via FAB"]
UC03["UC03: Alternar Conclusão da Tarefa"]
UC04["UC04: Editar Tarefa em Tela Própria"]
UC05["UC05: Excluir Tarefa com Diálogo Nativo"]
end
User --> UC01
User --> UC02
User --> UC03
User --> UC04
User --> UC05
HomeScreen carrega os dados invocando TaskService.getAll().FlatList.RefreshControl dispara onRefresh() para recarga instantânea.+ (FAB).TaskFormScreen.POST /api/tarefas.201 Created, o app retorna à tela inicial e atualiza a listagem.PATCH /api/tarefas/{id}/status com { "concluida": novoStatus }.TaskFormScreen é aberta com os parâmetros de rota { id: item.id }.GET /api/tarefas/{id} e preenchem os campos.PUT /api/tarefas/{id} e persiste as alterações.Alert.alert solicitando confirmação.DELETE /api/tarefas/{id} e a tarefa desaparece da lista.| Caso de Uso | História de Usuário | Requisito Funcional | Requisito Não-Funcional | Arquivo Responsável |
|---|---|---|---|---|
| UC01 | US-R01 | RF-R01 | RNF-R01, RNF-R02, RNF-R05 | HomeScreen.js, HomePage.jsx |
| UC02 | US-R02 | RF-R02 | RNF-R04, RNF-R05 | TaskFormScreen.js, TaskFormPage.jsx |
| UC03 | US-R03 | RF-R03 | RNF-R05 | HomeScreen.js, TaskService.js |
| UC04 | US-R04 | RF-R04 | RNF-R05 | TaskFormScreen.js, TaskFormPage.jsx |
| UC05 | US-R05 | RF-R05 | RNF-R03, RNF-R05 | HomeScreen.js, Alert.alert |
O projeto organiza-se em dois módulos front-end complementares consumindo a mesma API central:
fullstack_tarefas_07_spring_react/
├── assets/
│ ├── arquitetura_react_native_ecosystem.svg ← Diagrama conceitual React Web para Mobile
│ └── fluxo_sincronizacao_expo_api.svg ← Diagrama do ciclo de rede com Expo
├── web-react/ ← Web SPA em React 19 + Vite
│ ├── src/
│ │ ├── pages/
│ │ │ ├── HomePage.jsx ← Listagem com filtros e estilo moderno
│ │ │ └── TaskFormPage.jsx ← Formulário com validação e foco
│ │ ├── services/
│ │ │ ├── api.js ← Axios configurado em localhost:8080
│ │ │ └── TaskService.js ← Métodos HTTP de CRUD de tarefas
│ │ ├── App.jsx ← Configuração de rotas com React Router
│ │ └── main.jsx ← Ponto de entrada React 19
│ ├── package.json
│ └── vite.config.js
├── mobile-reactnative/ ← Cliente Mobile com React Native + Expo
│ ├── src/
│ │ ├── screens/
│ │ │ ├── HomeScreen.js ← FlatList, RefreshControl e FAB
│ │ │ └── TaskFormScreen.js ← Tela de formulário com Switch de status
│ │ └── services/
│ │ ├── api.js ← Resolução inteligente de host (10.0.2.2)
│ │ └── TaskService.js ← Cliente REST para tarefas
│ ├── App.js ← Navegação em pilha (Stack Navigator)
│ ├── app.json ← Configuração do ecossistema Expo
│ ├── package.json ← Dependências React Native e Expo
│ └── README.md ← Guia de execução mobile
└── index.md ← Este Guia Padrão Ouro Educacional
mobile-reactnative/src/services/api.js)O serviço detecta a plataforma em tempo de execução via Platform.OS:
import axios from 'axios';
import { Platform } from 'react-native';
const getBaseUrl = () => {
if (Platform.OS === 'android') {
return 'http://10.0.2.2:8080/api';
}
return 'http://localhost:8080/api';
};
const api = axios.create({
baseURL: getBaseUrl(),
headers: {
'Content-Type': 'application/json',
},
timeout: 10000,
});
export default api;
mobile-reactnative/src/screens/HomeScreen.js)A tela principal do cliente móvel implementa FlatList com RefreshControl:
<FlatList
data={tasks}
keyExtractor={item => item.id.toString()}
renderItem={renderItem}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
colors={['#6366f1']}
/>
}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}>Nenhuma tarefa encontrada</Text>
<Text style={styles.emptySub}>Puxe para atualizar ou toque no botão + para adicionar.</Text>
</View>
}
contentContainerStyle={{ paddingBottom: 80 }}
/>
Abra um terminal na pasta do backend oficial:
cd catalogo_projetos/fullstack_tarefas_01_spring_api
./mvnw spring-boot:run
✅ Checkpoint: Acesse
http://localhost:8080/api/tarefasno navegador e certifique-se de receber a lista em formato JSON com o modelo{ id, descricao, concluida }.
Em outro terminal:
cd catalogo_projetos/fullstack_tarefas_07_spring_react/frontend
npm install
npm run dev
✅ Checkpoint: Abra
http://localhost:5173no navegador e valide a listagem, os filtros por status, a criação e a exclusão de tarefas.
Em um terceiro terminal:
cd catalogo_projetos/fullstack_tarefas_07_spring_react/mobile
npm install
npx expo start
✅ Checkpoint: Pressione
apara iniciar no emulador Android já aberto. Teste o gesto de pull-to-refresh, a alternância de status tocando no checkbox e a adição de tarefas pelo FAB (+).
| Checkpoint | Ação / Cenário de Teste | Critério de Aceite | Status |
|---|---|---|---|
| CP-01 | Iniciar a API Spring Boot central na porta 8080 | Resposta 200 OK com array JSON na rota /api/tarefas. |
PASS ✅ |
| CP-02 | Executar a SPA React na Web via Vite (localhost:5173) |
Renderização de componentes, exibição de tarefas e filtros de status ativos. | PASS ✅ |
| CP-03 | Cadastrar e alternar status na Web SPA | Tarefa gravada via POST/PUT; refletida em tela instantaneamente. |
PASS ✅ |
| CP-04 | Iniciar cliente mobile via Expo no Emulador (10.0.2.2:8080) |
FlatList nativa renderiza as mesmas tarefas criadas no navegador web. |
PASS ✅ |
| CP-05 | Executar gesto de Pull-to-Refresh no React Native | Indicador giratório nativo acionado; dados recarregados da API central. | PASS ✅ |
| CP-06 | Cadastrar tarefa pelo botão FAB (+) no Expo | Nova tarefa persistida e renderizada sem perda de contexto ou reload. | PASS ✅ |
| Sintoma | Causa Mais Provável | Solução Recomendada |
|---|---|---|
Network Error no emulador Android |
O app tentou conectar em localhost:8080 ao invés de 10.0.2.2:8080. |
Verifique se Platform.OS === 'android' está configurado em src/services/api.js. |
| Erro de CORS no navegador Web | A API Spring Boot não permitiu a origem do Vite (:5173) ou Expo Web. |
Verifique se a anotação @CrossOrigin("*") está presente na API central. |
| Tela branca ou erro no React Router | Versão de dependência incompatível no Vite. | Execute npm install limpo na pasta web-react/. |
| Falha de conexão no celular físico | O celular não consegue resolver 10.0.2.2 ou localhost. |
Altere temporariamente a URL no api.js para o IP do seu computador na rede Wi-Fi (ex: 192.168.1.X:8080). |
Com a equivalência React ➔ React Native concluída com sucesso, avançaremos para o terceiro vértice da trilha:
fullstack_tarefas_08_spring_angular): Transição de Angular Standalone para Ionic Framework + Capacitor, com a filosofia “Use um framework completo com arquitetura definida”.