Arquitetura do Mobile¶
O aplicativo mobile do TepConfina e desenvolvido com Flutter 3, priorizando a experiencia offline-first essencial para o ambiente rural onde a conectividade e instavel.
Stack Tecnologica¶
| Tecnologia | Finalidade |
|---|---|
| Flutter 3.38 | Framework multiplataforma (iOS + Android) |
| Riverpod | Gerenciamento de estado reativo |
| Hive | Banco de dados local (NoSQL, rapido) |
| Dio | Cliente HTTP com interceptors |
| GoRouter | Roteamento declarativo |
| Firebase Cloud Messaging | Notificacoes push |
| Firebase Crashlytics | Reporte automatico de crashes Android |
| Flutter Secure Storage | Armazenamento seguro de tokens |
| TFLite Flutter | Inferencia local do cattle_detector.tflite (43 MB, Git LFS) |
mocktail 1.0.4 | Mocks para flutter_test |
Destinos de produção:
- Package:
br.com.tecnoepec.tepconfina - API:
--dart-define=ENV=productionaponta parahttps://tepconfina-api.tecnoepec.com.br - minSdk: 26 (Android 8.0+)
- Distribuição: Firebase App Distribution (grupo
testers) via CodePipeline; APK + AAB - Tema:
ThemeMode.system(segue dark mode do dispositivo) - 33 telas no total
Estrutura do Projeto¶
lib/
├── core/
│ ├── config/
│ │ ├── app_config.dart # Configuracoes da aplicacao
│ │ └── environment.dart # Variaveis de ambiente
│ ├── network/
│ │ ├── dio_client.dart # Instancia Dio configurada
│ │ ├── auth_interceptor.dart # Interceptor de autenticacao
│ │ ├── retry_interceptor.dart # Retry com backoff exponencial
│ │ └── offline_interceptor.dart # Interceptor de fila offline
│ ├── storage/
│ │ ├── hive_service.dart # Servico de acesso ao Hive
│ │ └── secure_storage.dart # Wrapper do flutter_secure_storage
│ ├── sync/
│ │ ├── sync_service.dart # Orquestrador de sincronizacao
│ │ ├── sync_queue.dart # Fila de operacoes pendentes
│ │ └── conflict_resolver.dart # Resolucao de conflitos
│ └── theme/
│ └── app_theme.dart # Tema e cores do aplicativo
│
├── features/
│ ├── auth/
│ │ ├── data/ # Repository, data sources
│ │ ├── domain/ # Entities, use cases
│ │ └── presentation/ # Pages, widgets, providers
│ ├── dashboard/
│ ├── lotes/
│ ├── animais/
│ ├── pesagens/
│ ├── racoes/
│ └── notificacoes/
│
├── shared/
│ ├── widgets/ # Widgets reutilizaveis
│ ├── utils/ # Utilitarios (formatadores, etc.)
│ └── providers/ # Providers globais
│
├── router/
│ └── app_router.dart # Configuracao do GoRouter
│
└── main.dart # Entry point
Feature-based Structure
Cada feature segue uma mini clean architecture com data/, domain/ e presentation/, mantendo o codigo encapsulado e facilmente testavel.
Arquitetura Offline-First¶
A estrategia offline-first garante que o aplicativo funcione sem internet, sincronizando dados quando a conexao e restabelecida.
graph TB
subgraph "Dispositivo"
UI[Interface do Usuario]
PROV[Riverpod Providers]
REPO[Repository]
HIVE[(Hive<br/>Dados Locais)]
QUEUE[Sync Queue<br/>Operacoes Pendentes]
end
subgraph "Servidor"
API[Backend API]
DB[(PostgreSQL)]
end
UI --> PROV
PROV --> REPO
REPO -->|Leitura| HIVE
REPO -->|Escrita offline| QUEUE
REPO -->|Online| API
API --> DB
QUEUE -.->|Sincronizacao| API
API -.->|Atualizacao| HIVE
style HIVE fill:#E65100,color:#fff
style QUEUE fill:#F57F17,color:#fff
style API fill:#0277BD,color:#fff Fluxo de Leitura¶
- O provider solicita dados ao repository
- O repository verifica o cache local (Hive) primeiro
- Se os dados estao no cache e sao recentes, retorna imediatamente
- Em paralelo (se online), busca dados atualizados da API
- Atualiza o cache local com os dados novos
Fluxo de Escrita (Online)¶
- O usuario cria/edita um registro
- O repository envia para a API
- Em caso de sucesso, atualiza o cache local
- O provider notifica a UI
Fluxo de Escrita (Offline)¶
- O usuario cria/edita um registro
- O repository salva no cache local com flag
pendingSync - A operacao e adicionada a sync queue
- A UI e atualizada imediatamente (otimista)
- Quando a conexao e restabelecida, o
SyncServiceprocessa a fila
Fila de Sincronizacao¶
A sync queue armazena operacoes pendentes no Hive com a seguinte estrutura:
| Campo | Tipo | Descricao |
|---|---|---|
id | String | UUID unico da operacao |
entity | String | Tipo da entidade (lote, animal, etc) |
operation | Enum | CREATE, UPDATE, DELETE |
payload | Map | Dados da operacao serializada |
createdAt | DateTime | Momento da criacao |
retryCount | int | Numero de tentativas realizadas |
status | Enum | PENDING, SYNCING, FAILED, SYNCED |
stateDiagram-v2
[*] --> Pending: Operacao criada offline
Pending --> Syncing: Conexao disponivel
Syncing --> Synced: Sucesso (HTTP 2xx)
Syncing --> Failed: Erro (HTTP 4xx/5xx)
Failed --> Pending: Retry automatico
Synced --> [*]: Removida da fila Resolucao de Conflitos
Em caso de conflito (dado alterado no servidor enquanto estava offline), o sistema adota a estrategia last-write-wins com base no timestamp. Conflitos sao registrados no log e o usuario e notificado para revisao manual quando necessario.
Interceptors do Dio¶
O cliente HTTP utiliza tres interceptors encadeados:
graph LR
REQ[Request] --> AUTH[Auth Interceptor]
AUTH -->|Adiciona JWT| RETRY[Retry Interceptor]
RETRY --> OFFLINE[Offline Interceptor]
OFFLINE -->|Online| API[Backend API]
OFFLINE -->|Offline| QUEUE[Sync Queue]
API -->|401| AUTH
AUTH -->|Refresh token| API | Interceptor | Responsabilidade |
|---|---|
| Auth Interceptor | Adiciona header Authorization: Bearer <token>. Em caso de 401, tenta refresh automatico. |
| Retry Interceptor | Retry com backoff exponencial para erros de rede (timeout, connection reset). Maximo 3 tentativas. |
| Offline Interceptor | Detecta ausencia de conexao e redireciona mutacoes (POST/PUT/DELETE) para a sync queue. |
Notificacoes Push¶
O aplicativo utiliza Firebase Cloud Messaging (FCM) para receber notificacoes push:
- Foreground: Exibidas como banner in-app
- Background: Notificacao nativa do sistema operacional
- Terminated: Notificacao nativa com deep link para a tela relevante
| Tipo de Notificacao | Origem |
|---|---|
| GMD vermelho | ProactiveAlertService (backend, hora em hora) |
| Lote proximo da saida | ProactiveAlertService |
| Variacao brusca de preco | ProactiveAlertService quando BGI varia >2% em 24h |
| Estoque minimo | ProactiveAlertService |
| Mortalidade fora do padrao | ProactiveAlertService |
| Alerta de preco do usuario | AlertaPrecoService quando o BGI atinge o valor configurado |
| Digest matinal | DailyDigestService (5:30 BRT) — resumo do dia anterior |
| Sincronizacao | Hive sync queue local |
Armazenamento Seguro¶
Dados sensiveis sao armazenados utilizando Flutter Secure Storage:
| Dado | Storage | Motivo |
|---|---|---|
| JWT Access Token | Secure Storage | Credencial de acesso |
| Refresh Token | Secure Storage | Credencial de renovacao |
| Dados de cache | Hive | Performance, acesso offline |
| Sync queue | Hive | Persistencia de operacoes pendentes |
| Preferencias | Hive | Configuracoes do usuario |
Seguranca no Hive
O Hive suporta encriptacao AES-256 para boxes sensiveis. No TepConfina, os boxes que armazenam dados de animais e financeiros sao encriptados, enquanto boxes de configuracao e cache utilizam armazenamento padrao para melhor performance.
Navegacao com GoRouter¶
O GoRouter fornece roteamento declarativo com suporte a deep links:
/login → LoginPage
/ → DashboardPage
/lotes → LotesListPage
/lotes/:id → LoteDetalhePage
/lotes/:id/animais → AnimaisDoLotePage
/animais → AnimaisListPage
/animais/:id → AnimalDetalhePage
/pesagens → PesagensPage
/pesagens/nova → NovaPesagemPage
/racoes → RacoesPage
/notificacoes → NotificacoesPage
Modo Curral¶
Tela otimizada para uso dentro do curral (luva, mão suja, tela ao sol). UI simplificada com 4 botões gigantes e suporte a comando de voz.
| Botão | Ação |
|---|---|
| 🐂 Pesagem | Câmera/teclado numérico para registrar peso individual (busca brinco por voz) |
| 💀 Mortalidade | Registra morte com data atual e causa por dropdown |
| 💉 Medicamento | Aplica medicamento a vários animais de uma vez (lê brincos por voz) |
| ↩ Sair | Volta ao app normal |
Comando de voz via Android STT (speech_to_text):
- "pesar B001 quatrocentos e vinte" → registra B001 com 420 kg
- "morte B042 pneumonia"
- "vacina B055 B056 B057"
Todas as ações entram na sync queue Hive, garantindo offline-first total.
Conferência Visual (TF-Lite)¶
Detecção de cabeças de gado a partir de foto do lote, executada localmente no dispositivo (sem latência de rede e sem custo por imagem).
- Modelo:
assets/cattle_detector.tflite(43 MB, gerenciado via Git LFS —git lfs pullnecessário em CI) - Service:
cattle_detector_service.dart - Pipeline: câmera → resize 320×320 → inferência TFLite → bounding boxes → contagem
- ProGuard: regras específicas em
android/app/proguard-rules.propara não obfuscar classes do TFLite (CodeBuild quebra sem isso)
A foto também é enviada para o backend para análise BCS via Claude Vision (assíncrono).
Crashlytics e Boot Safety¶
Reporte automático¶
Inicializado em main.dart antes do runApp. Crashes Android (uncaught exceptions, ANRs) caem em console.firebase.google.com/project/tep-confina/crashlytics.
Boot defensivo¶
main.dart envolve a inicialização de plugins críticos (Firebase, Hive, secure storage) em try/catch. Se algum falha, o app sobe em modo degradado com banner de erro em vez de tela branca:
try {
await Firebase.initializeApp();
await FirebaseCrashlytics.instance.setCrashlyticsCollectionEnabled(true);
FlutterError.onError = FirebaseCrashlytics.instance.recordFlutterError;
} catch (e, stack) {
// App sobe sem Crashlytics — log local e segue
debugPrint('Firebase init failed: $e\n$stack');
}
Build e Distribuição¶
| Comando | Descrição |
|---|---|
flutter run | Dev local apontando para localhost |
flutter build apk --dart-define=ENV=production | APK para distribuição |
flutter build appbundle --dart-define=ENV=production | AAB para Play Store |
flutter test | 139 testes unitários |
Pipeline CodeBuild (buildspec.yml no tepconfina-mobile):
- Instala Android SDK + Flutter
- Baixa
google-services.jsondo Secrets Manager - Decodifica keystore base64 do Secrets Manager (
development.TEPCONFINA_KEYSTORE) para assinar release - Roda testes (139 unit + 8 integration em device real quando disponível)
- Build APK + AAB com ProGuard
- Upload para Firebase App Distribution → grupo
testers
Paginas relacionadas: Visao Geral | Backend | Frontend