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

📝 Projeto Lista de Tarefas 08 — Spring Boot + Angular ➔ Ionic Framework

Seja muito bem-vindo à Parte 08 da série de projetos Lista de Tarefas!

Nesta etapa prática, você aprenderá a dominar a terceira grande trilha de equivalência entre desenvolvimento Web e Mobile Híbrido / Cross-platform:

\[\text{Web SPA (Angular 20 Standalone)} \longrightarrow \text{Mobile Híbrido Cross-Platform (Ionic Framework 8 + Capacitor)}\]

💡 “Use um framework completo com arquitetura definida.”

Enquanto no Angular tradicional para desktop desenvolvemos interfaces baseadas em tabelas, formulários convencionais e navegação em tela cheia, o ecossistema móvel exige uma experiência tátil rica: gestos de deslize (swipe actions), puxar para atualizar (pull-to-refresh), diálogos nativos modais e notificações toast. O Ionic Framework sobre o Capacitor permite que toda a elegância da arquitetura Angular (TypeScript, injeção de dependências e programação reativa com RxJS) seja transferida diretamente para o smartphone, gerando pacotes nativos para Android (APK/AAB) e iOS (IPA) com uma única base de código.


🗺️ 1. Visão Geral da Arquitetura & Papel no Ecossistema

Tanto o aplicativo Web SPA em Angular quanto o cliente móvel híbrido em Ionic operam consumindo a nossa Fonte Única da Verdade (SSOT): a API REST Spring Boot na porta 8080, conectada ao banco de dados relacional e padronizada com o modelo canônico { id, descricao, concluida }.

Ecossistema Angular e Ionic Mobile

Por que a combinação Angular + Ionic Framework?


⚡ 2. Fluxo Reativo, Gestos Mobile e Resolução de Plataforma

No desenvolvimento mobile híbrido com Capacitor, a resolução de endereço de rede depende do ambiente de execução do aplicativo:

Fluxo de Gestos e Comunicação REST

  1. No Navegador Web / PWA: O app faz chamadas HTTP diretamente para http://localhost:8080/api/tarefas.
  2. No Emulador Android (AVD): O emulador opera em uma rede virtual isolada onde localhost refere-se ao próprio dispositivo virtual. O Capacitor detecta a plataforma via Capacitor.isNativePlatform() e redireciona automaticamente para o gateway da máquina hospedeira: \(\text{URL no Emulador Android} = \mathbf{http://10.0.2.2:8080/api/tarefas}\)
  3. No Dispositivo Físico (Wi-Fi): Configura-se o IP privado da máquina de desenvolvimento (ex: http://192.168.1.100:8080/api/tarefas).

📑 3. Engenharia de Software & Especificação de Requisitos

3.1. Histórias de Usuário (Web & Mobile Ionic)

3.2. Requisitos Funcionais (RF)

ID Descrição Endpoint REST
RF-I01 Listar tarefas ordenadas com pull-to-refresh (ion-refresher) e filtros de exibição. GET /api/tarefas
RF-I02 Criar nova tarefa com validação de preenchimento obrigatório da descrição. POST /api/tarefas
RF-I03 Alternar status concluída/pendente via endpoint patch atômico ou atualização put. PATCH /api/tarefas/{id}/status
RF-I04 Modificar a descrição da tarefa através de AlertController interativo. PUT /api/tarefas/{id}
RF-I05 Deletar permanentemente uma tarefa com confirmação prévia e feedback por toast. DELETE /api/tarefas/{id}

3.3. Requisitos Não-Funcionais (RNF)

ID Categoria Descrição
RNF-I01 Arquitetura Standalone O frontend e o mobile devem utilizar Angular Standalone Components sem dependência de NgModule.
RNF-I02 Ergonomia Móvel A interface do cliente Ionic deve responder a gestos táteis nativos (ion-item-sliding, ion-refresher).
RNF-I03 Resolução Inteligente de Host O serviço TarefaService deve alternar dinamicamente entre localhost:8080 e 10.0.2.2:8080 conforme a plataforma nativa.
RNF-I04 Feedback Assíncrono Todas as operações de criação, erro ou exclusão devem emitir mensagens flutuantes via ToastController.
RNF-I05 Segurança de Rede (Cleartext) A configuração do Capacitor Android deve liberar conexões HTTP em desenvolvimento através de cleartext: true.

3.4. Casos de Uso do Sistema

flowchart LR
    User(("👤 Usuário Web / Mobile"))

    subgraph "Aplicação Angular / Ionic"
        UC01["UC01: Listar Tarefas com Pull-to-Refresh"]
        UC02["UC02: Inserir Nova Tarefa"]
        UC03["UC03: Alternar Status via Checkbox"]
        UC04["UC04: Editar Tarefa com Swipe Action"]
        UC05["UC05: Excluir Tarefa com Swipe Action"]
    end

    User --> UC01
    User --> UC02
    User --> UC03
    User --> UC04
    User --> UC05

UC01: Listar Tarefas com Pull-to-Refresh

UC02: Inserir Nova Tarefa

UC03: Alternar Status via Checkbox

UC04: Editar Tarefa com Swipe Action

UC05: Excluir Tarefa com Swipe Action


3.5. Matriz de Rastreabilidade (RTM)

Caso de Uso História de Usuário Requisito Funcional Requisito Não-Funcional Arquivo Responsável
UC01 US-I01 RF-I01 RNF-I01, RNF-I02, RNF-I03 tarefas.page.ts, tarefas.page.html
UC02 US-I02 RF-I02 RNF-I01, RNF-I04 tarefas.page.ts, tarefa.service.ts
UC03 US-I03 RF-I03 RNF-I01, RNF-I03 tarefas.page.ts, ion-checkbox
UC04 US-I04 RF-I04 RNF-I01, RNF-I02 tarefas.page.ts, AlertController
UC05 US-I05 RF-I05 RNF-I01, RNF-I04 tarefas.page.ts, ion-item-sliding

🛠️ 4. Estrutura de Pastas da Parte 08

O projeto está dividido entre a aplicação Web SPA tradicional e o cliente móvel híbrido:

fullstack_tarefas_08_spring_angular/
├── assets/
│   ├── arquitetura_angular_ionic_ecosystem.svg  ← Diagrama conceitual Angular para Ionic
│   └── fluxo_gestos_ionic_api.svg               ← Diagrama de gestos móveis e REST
├── web-angular/                                    ← Web SPA Standalone (Angular 20)
│   ├── src/
│   │   ├── app/
│   │   │   ├── components/
│   │   │   │   ├── tarefa-form/                 ← Formulário Web de criação/edição
│   │   │   │   └── tarefa-list/                 ← Tabela responsiva de listagem
│   │   │   ├── models/tarefa.model.ts           ← Interface { id, descricao, concluida }
│   │   │   └── services/tarefa.service.ts       ← Cliente HttpClient (Porta 8080)
│   │   └── main.ts                              ← Bootstrap Angular Standalone
│   └── package.json
├── mobile-ionic/                                ← Cliente Mobile Híbrido (Ionic 8 + Capacitor)
│   ├── capacitor.config.json                    ← Configuração com cleartext: true
│   ├── ionic.config.json                        ← Integração Ionic CLI
│   ├── package.json                             ← Dependências Ionic, Angular e Capacitor
│   └── src/
│       ├── app/
│       │   ├── models/tarefa.model.ts           ← Modelo unificado com a API central
│       │   ├── pages/tarefas/
│       │   │   ├── tarefas.page.ts              ← Lógica mobile, gestos e diálogos
│       │   │   ├── tarefas.page.html            ← Template com ion-item-sliding e refresher
│       │   │   └── tarefas.page.scss            ← Estilização mobile refinada
│       │   ├── services/tarefa.service.ts       ← Detecção de host (10.0.2.2 vs localhost)
│       │   ├── app.component.ts                 ← Container ion-app e router-outlet
│       │   └── app.routes.ts                    ← Roteamento do app
│       ├── theme/variables.css                  ← Paleta de cores do Ionic
│       └── main.ts                              ← provideIonicAngular e provideHttpClient
└── index.md                                     ← Guia Didático Padrão Ouro Educacional

💻 5. Implementação Prática & Destaques de Código

5.1. Resolução Dinâmica de Rede no Ionic (mobile-ionic/src/app/services/tarefa.service.ts)

O serviço detecta em tempo de execução a plataforma nativa do Capacitor:

import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Capacitor } from '@capacitor/core';
import { Tarefa } from '../models/tarefa.model';

@Injectable({
  providedIn: 'root'
})
export class TarefaService {
  private http = inject(HttpClient);

  private get baseUrl(): string {
    const isNative = Capacitor.isNativePlatform();
    const platform = Capacitor.getPlatform();
    if (isNative && platform === 'android') {
      return 'http://10.0.2.2:8080/api/tarefas';
    }
    return 'http://localhost:8080/api/tarefas';
  }

  listar(): Observable<Tarefa[]> {
    return this.http.get<Tarefa[]>(this.baseUrl);
  }

  criar(tarefa: Tarefa): Observable<Tarefa> {
    return this.http.post<Tarefa>(this.baseUrl, tarefa);
  }

  atualizar(id: number, tarefa: Tarefa): Observable<Tarefa> {
    return this.http.put<Tarefa>(`${this.baseUrl}/${id}`, tarefa);
  }

  alternarStatus(id: number, concluida: boolean): Observable<Tarefa> {
    return this.http.patch<Tarefa>(`${this.baseUrl}/${id}/status`, { concluida });
  }

  excluir(id: number): Observable<void> {
    return this.http.delete<void>(`${this.baseUrl}/${id}`);
  }
}

5.2. Gestos de Swipe no Template Ionic (tarefas.page.html)

A experiência móvel tira proveito do componente ion-item-sliding:

<ion-list lines="full">
  <ion-item-sliding *ngFor="let tarefa of tarefasFiltradas" #slidingItem>
    <ion-item class="tarefa-item" [class.concluida]="tarefa.concluida">
      <ion-checkbox
        slot="start"
        [checked]="tarefa.concluida"
        (ionChange)="alternarStatus(tarefa)"
      ></ion-checkbox>
      <ion-label>
        <h2>{{ tarefa.descricao }}</h2>
        <p>ID #{{ tarefa.id }} · Deslize para opções</p>
      </ion-label>
      <ion-badge slot="end" [color]="tarefa.concluida ? 'success' : 'warning'">
        {{ tarefa.concluida ? 'Concluída' : 'Pendente' }}
      </ion-badge>
    </ion-item>

    <ion-item-options side="end">
      <ion-item-option color="primary" (click)="abrirEdicao(tarefa, slidingItem)">
        <ion-icon slot="icon-only" name="create-outline"></ion-icon>
      </ion-item-option>
      <ion-item-option color="danger" (click)="confirmarExclusao(tarefa, slidingItem)">
        <ion-icon slot="icon-only" name="trash-outline"></ion-icon>
      </ion-item-option>
    </ion-item-options>
  </ion-item-sliding>
</ion-list>

🚀 6. Roteiro de Execução e Homologação

1. Iniciar a API Spring Boot Central (SSOT)

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/tarefas no navegador e certifique-se de receber a lista em formato JSON com as propriedades id, descricao e concluida.

2. Executar o Frontend Web Angular Tradicional

Em outro terminal:

cd catalogo_projetos/fullstack_tarefas_08_spring_angular/frontend
npm install
npm start

✅ Checkpoint: Abra http://localhost:4200 e teste o cadastro e a listagem das tarefas na interface Web.

3. Executar o Cliente Mobile Ionic no Navegador

Em um terceiro terminal:

cd catalogo_projetos/fullstack_tarefas_08_spring_angular/mobile-ionic
npm install
npm start

✅ Checkpoint: Acesse http://localhost:4200 (ou a porta atribuída pelo Ionic CLI), abra o Device Mode nas ferramentas do desenvolvedor (F12) e teste os gestos de arrasto e os componentes Ionic.

4. Sincronizar e Rodar no Emulador Android

Com o Android Studio e o emulador instalados:

npm run build
npx cap sync android
npx cap open android

No Android Studio, clique em Run ‘app’ (ícone do play verde). O aplicativo inicializará no emulador conectando-se ao host via http://10.0.2.2:8080/api/tarefas.

5. Matriz de Homologação & Checkpoints de Validação

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 SPA Angular Standalone (localhost:4200) Injeção via inject(), renderização limpa e listagem de dados ativa. PASS ✅
CP-03 Cadastrar e gerenciar tarefas na Web Angular Operações POST/PUT disparadas e refletidas sem recarregar tela. PASS ✅
CP-04 Executar cliente Ionic no modo Device (localhost:4200) Componentes móveis (ion-card, ion-badge) renderizados com visual nativo. PASS ✅
CP-05 Testar gesto de swipe no ion-item-sliding Ações de edição e exclusão reveladas de forma fluida ao deslizar o item. PASS ✅
CP-06 Rodar app via Capacitor no Emulador Android (10.0.2.2) App conecta perfeitamente, sincroniza dados e respeita regras de rede. PASS ✅

🧯 7. Tabela de Troubleshooting

Sintoma Causa Mais Provável Solução Recomendada
ERR_CONNECTION_REFUSED no emulador O app tentou conectar em localhost:8080 ao invés de 10.0.2.2:8080. Verifique se Capacitor.isNativePlatform() está ativo no tarefa.service.ts.
Cleartext HTTP traffic not permitted O Android 9+ bloqueia tráfego HTTP sem SSL por padrão. Garanta "server": { "cleartext": true } no capacitor.config.json.
Erro de CORS no console do navegador O backend não aceitou a origem do cliente Ionic/Angular. Certifique-se de que a API Spring Boot contém @CrossOrigin("*") ou configuração de WebConfig.
Itens deslizantes não respondem Falta da importação standalone do IonItemSliding e IonItemOptions. Verifique o array imports do componente TarefasPage.

🏁 8. Conclusão da Tríade de Equivalências Web ➔ Mobile Cross-Platform

Com a entrega da Parte 08 (Angular ➔ Ionic Framework), consolidamos a trilha completa de arquitetura híbrida e cross-platform:

👉 Próximo Passo: Parte 09 — Evolução Arquitetural Desktop: De Swing para JavaFX Moderno

Avançamos a seguir para a modernização da engenharia desktop corporativa em Java: aprendendo como e por que o mercado migrou do Swing clássico para JavaFX moderno com FXML, folhas de estilo CSS, propriedades reativas e arquitetura MVC rigorosa!