Documentação Abrangente pwE
pwE (Pray With Me) é um aplicativo revolucionário desenvolvido em Flutter, projetado especificamente para guiar o usuário na prática milenar da Lectio Divina (Leitura Orante). Em perfeita consonância com a fé católica e o Magistério da Igreja, o app une espiritualidade e alta tecnologia para criar um ambiente imersivo, focado e seguro de oração.
Princípios e Pilares do Projeto
Desenvolvido sob rígidas normas de arquitetura e design (conforme o tema oficial definido em app_theme.dart), o pwE é sustentado por três pilares fundamentais:
† Fidelidade Doutrinária
Todas as meditações, orações e reflexões propostas pela IA seguem rigorosamente o Magistério Católico. Nenhuma metáfora ou termo herético ou tecnológico é exposto ao usuário na experiência de meditação.
🔒 Privacidade Absoluta
Oração é intimidade. Com o motor Edge LLM, o processamento de IA pode ocorrer inteiramente de forma local no dispositivo do usuário, garantindo a soberania dos dados da alma.
📱 Design Imersivo
Utilização de mecânicas refinadas (como revelação gradual por scroll e gestos táteis de retenção) para eliminar distrações e guiar o usuário pelas etapas litúrgicas.
Clean Architecture & DDD
Para garantir que o negócio seja independente de tecnologias de banco de dados, frameworks ou APIs de terceiros, o pwE adota uma estrutura em camadas baseada em Clean Architecture e Domain-Driven Design (DDD).
Camadas Arquiteturais
A separação de responsabilidades assegura a facilidade de manutenção e alta cobertura de testes unitários:
-
Camada de Domínio (Domain): Contém as regras de negócio puras (
Entities,UseCasese contratos definidos). Veja a entidade da Lectio Divina no arquivo lectio_session.dart:// lib/features/lectio_divina/domain/entities/lectio_session.dart class LectioSession { final String id; final DateTime date; final String passage; final String emotion; final String meditation; final String prayer; final String contemplationResolution; final int streakDays; const LectioSession({ required this.id, required this.date, required this.passage, required this.emotion, required this.meditation, required this.prayer, required this.contemplationResolution, required this.streakDays, }); } -
Camada de Dados (Data): Contém a infraestrutura (
Models,DataSourcese as implementações concretas dos repositórios como em llm_remote_datasource.dart):// lib/features/lectio_divina/data/datasources/llm_remote_datasource.dart abstract class LlmRemoteDataSource { Future<MeditationModel> generateMeditation(MeditationRequestModel request); } class LlmRemoteDataSourceImpl implements LlmRemoteDataSource { final ApiClient apiClient; LlmRemoteDataSourceImpl({required this.apiClient}); @override Future<MeditationModel> generateMeditation(MeditationRequestModel request) async { final response = await apiClient.post( '/v1/spiritual-advisor/meditate', data: request.toJson(), ); return MeditationModel.fromJson(response.data); } } -
Camada de Apresentação (Presentation): Contém a interface do usuário (Widgets e Pages) e a gestão de estado complexa baseada em **Bloc** (veja em lectio_bloc.dart):
// lib/features/lectio_divina/presentation/bloc/lectio_bloc.dart class LectioBloc extends Bloc<LectioEvent, LectioState> { final GetGuidedMeditation getGuidedMeditation; final SaveSession saveSession; LectioBloc({ required this.getGuidedMeditation, required this.saveSession, }) : super(LectioInitial()) { on<StartSessionEvent>(_onStartSession); on<AdvanceToStepEvent>(_onAdvanceToStep); on<CompleteSessionEvent>(_onCompleteSession); } void _onStartSession(StartSessionEvent event, Emitter<LectioState> emit) { emit(LectioInProgress(step: LectioStep.reading, passage: event.passage)); } }
Todos os serviços, repositórios, use cases e blocs são registrados no container central de injeção localizado em injection_container.dart através do pacote **GetIt**:
// lib/core/di/injection_container.dart
final sl = GetIt.instance;
Future<void> init() async {
// Features - Lectio Divina (BLoCs)
sl.registerFactory(() => LectioBloc(getGuidedMeditation: sl(), saveSession: sl()));
// Use cases
sl.registerLazySingleton(() => GetGuidedMeditation(sl()));
sl.registerLazySingleton(() => SaveSession(sl()));
// Repositories
sl.registerLazySingleton<IARepository>(() => IARepositoryImpl(remoteDataSource: sl()));
// Data sources
sl.registerLazySingleton<LlmRemoteDataSource>(() => LlmRemoteDataSourceImpl(apiClient: sl()));
// External (Core)
sl.registerLazySingleton(() => ApiClient());
}
Fluxo Imersivo da Lectio Divina
A Lectio Divina no pwE consolida as quatro etapas clássicas em uma visualização contínua usando a mecânica de CustomScrollView, eliminando a fadiga de telas:
Lectio (Leitura)
O usuário lê com atenção o texto sagrado do dia (liturgia diária) ou digita/digitaliza uma passagem desejada.
Meditatio (Meditação)
O app sugere pontos de meditação através da IA, conectando o texto sagrado a analogias e vivências de fé.
Oratio (Oração)
O usuário responde à Palavra em forma de súplica, adoração ou agradecimento, com prompts baseados nos Salmos.
Contemplatio (Ação)
O usuário traça uma resolução prática de vida e guarda a oração no silêncio do seu diário pessoal.
Componentes da UI Focada
Estes elementos de interface criam uma atmosfera de introspecção na página immersive_reading_page.dart:
- ProgressiveTextReveal: Esconde trechos futuros por meio de desfoque (blur) até que o usuário faça scroll ou interaja com o texto, auxiliando a focar na leitura atual.
- HoldToSaveButton: Exige pressionar o botão por 2 segundos contínuos com resposta tátil para assegurar a intencionalidade de avanço de etapa, bloqueando cliques indesejados no silêncio da oração.
- ThinkingFeedback: Exibe mensagens motivacionais rotativas e espirituais enquanto a IA processa o prompt (Ex: "Unindo a sua oração aos coros celestes...").
Veja um exemplo prático de implementação do HoldToSaveButton, com lógica tátil e preenchimento gradual por tempo em Flutter:
// lib/features/lectio_divina/presentation/widgets/hold_to_save_button.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class HoldToSaveButton extends StatefulWidget {
final VoidCallback onTrigger;
final String label;
const HoldToSaveButton({Key? key, required this.onTrigger, required this.label}) : super(key: key);
@override
_HoldToSaveButtonState createState() => _HoldToSaveButtonState();
}
class _HoldToSaveButtonState extends State<HoldToSaveButton> with SingleTickerProviderStateMixin {
late AnimationController _controller;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 2),
);
_controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
HapticFeedback.heavyImpact();
widget.onTrigger();
_controller.reset();
}
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onLongPressStart: (_) {
HapticFeedback.mediumImpact();
_controller.forward();
},
onLongPressEnd: (_) {
if (_controller.status != AnimationStatus.completed) {
_controller.reverse();
}
},
child: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Container(
height: 56,
width: double.infinity,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.amber.shade700, width: 1.5),
gradient: LinearGradient(
colors: [Colors.amber.shade800, Colors.amber.shade900],
stops: [0.0, _controller.value],
),
),
child: Center(
child: Text(
_controller.value > 0 ? 'Mantenha pressionado... ${(_controller.value * 100).toInt()}%' : widget.label,
style: const TextStyle(fontWeight: FontWeight.bold, color: Colors.white),
),
),
);
},
),
);
}
}
Sistema de Maturidade (Fading Scaffolding)
Para não sobrecarregar o usuário e encorajá-lo a rezar de forma autônoma, o pwE implementa a estratégia inovadora de Fading Scaffolding. A IA adapta o nível de verbosidade e a profundidade de seus prompts conforme o progresso espiritual do fiel.
Os Três Estágios da Maturidade
Beginner (Iniciante)
Orientação didática detalhada. A IA ensina o significado de cada etapa e apresenta analogias ilustrativas detalhadas para iniciantes na prática.
Practitioner (Praticante)
Foco em provocações de fé. Elimina as introduções acadêmicas e propõe pontos diretos de questionamento pessoal e aplicação na caridade diária.
Contemplative (Contemplativo)
Minimalismo e Silêncio. A IA fornece no máximo duas linhas inspiradoras baseadas na tradição dos Padres da Igreja, estimulando o silêncio interior.
A evolução ocorre naturalmente com a contagem de constância (streak). Contudo, o usuário pode definir manualmente seu nível de maturidade nas configurações caso deseje um apoio mais contundente ou prefira o silêncio contemplativo imediatamente.
Invasão Zero & Privacidade: Edge LLM
Um dos maiores diferenciais do pwE é a sua capacidade de operar de maneira 100% autônoma e privada. Graças ao motor Edge LLM, as orações escritas na intimidade do quarto nunca precisam sair do smartphone.
Especificações Técnicas de Inferência Local
- Modelo Utilizado: Gemma 4 (classe 2B) otimizado e quantizado para dispositivos móveis.
- Engine: Baseado na biblioteca **LiteRT-LM** rodando em canais de métodos nativos (`MethodChannel`) rápidos e eficientes. Gerenciado por llm_local_datasource.dart.
- Hardware Mínimo: Smartphones com 4GB de RAM livre, suporte a aceleração gráfica por GPU/NPU e ~1.3GB de espaço livre para o modelo quantizado.
- Indicador Visual de Privacidade: Sempre que a IA é processada localmente, o app apresenta um Escudo Dourado, atestando o isolamento do processamento.
Abaixo está a implementação prática de LlmLocalDataSourceImpl, demonstrando a invocação nativa e o fallback automático para a nuvem em caso de erro:
// lib/features/lectio_divina/data/datasources/llm_local_datasource.dart
import 'package:flutter/services.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_model.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_request_model.dart';
import 'package:pwe/features/lectio_divina/data/datasources/llm_remote_datasource.dart';
abstract class LlmLocalDataSource {
Future<MeditationModel> generateMeditation(MeditationRequestModel request);
}
class LlmLocalDataSourceImpl implements LlmLocalDataSource {
static const MethodChannel _channel = MethodChannel('pwe.app/gemma-inference');
final LlmRemoteDataSource remoteFallback;
final bool isGemmaEnabled;
LlmLocalDataSourceImpl({
required this.remoteFallback,
required this.isGemmaEnabled,
});
@override
Future<MeditationModel> generateMeditation(MeditationRequestModel request) async {
if (!isGemmaEnabled) {
return remoteFallback.generateMeditation(request);
}
try {
final String? response = await _channel.invokeMethod<String>(
'executeGemmaInference',
{
'prompt': request.toPrompt(),
'max_tokens': 200,
'temperature': 0.3,
},
);
if (response == null || response.isEmpty) {
throw PlatformException(code: 'EMPTY_RESPONSE', message: 'Gemma local retornou vazio.');
}
return MeditationModel.fromLocalText(response, request.passage);
} catch (e) {
// Fallback transparente em caso de indisponibilidade ou falha de inferência local
return remoteFallback.generateMeditation(request);
}
}
}
Se o modelo local não for encontrado ou se o dispositivo não comportar a inferência, o aplicativo faz uma transição transparente e segura para a API na nuvem (Gemini), assegurando a continuidade da Lectio Divina sem interrupções.
Captura de Passagem Bíblica por Foto (OCR)
Além da digitação de passagens e da liturgia do dia coletada dinamicamente, o pwE permite a captura instantânea de textos bíblicos impressos usando inteligência artificial visual local.
Funcionamento e Arquitetura do OCR
Integrando o Google ML Kit Vision, o processo é inteiramente offline e ágil:
1. Captura & Edição
O usuário fotografa o trecho da Bíblia física e edita a imagem (corta e rotaciona) na tela de pré-processamento para focar exclusivamente no texto litúrgico.
2. Extração de Texto
O processamento OCR extrai as sentenças com precisão, aplicando um refinamento automático de ortografia e pontuação para a leitura fluida.
3. Identificação Remota
O trecho reconhecido é enviado para o analisador de contexto bíblico, que detecta automaticamente o livro, capítulo e versículo correspondente.
Abaixo está o exemplo prático de integração com o ML Kit OCR para extração offline de texto bíblico nas páginas físicas da Bíblia:
// lib/features/lectio_divina/data/datasources/ocr_local_datasource.dart
import 'dart:io';
import 'package:google_ml_kit/google_ml_kit.dart';
abstract class OcrLocalDataSource {
Future<String> extractTextFromImage(File imageFile);
}
class OcrLocalDataSourceImpl implements OcrLocalDataSource {
final TextRecognizer _textRecognizer = GoogleMlKit.vision.textRecognizer();
@override
Future<String> extractTextFromImage(File imageFile) async {
final inputImage = InputImage.fromFile(imageFile);
try {
final RecognizedText recognizedText = await _textRecognizer.processImage(inputImage);
// Filtra e limpa o texto para remover ruídos e marcas indesejadas do reconhecimento
String rawText = recognizedText.text;
return _refineBiblicalText(rawText);
} catch (e) {
throw Exception('Erro ao processar imagem via ML Kit OCR: ${e.toString()}');
}
}
String _refineBiblicalText(String input) {
return input
.replaceAll('\n', ' ')
.replaceAll(RegExp(r'\s+'), ' ')
.trim();
}
void dispose() {
_textRecognizer.close();
}
}
Contratos de Integração & API (Swagger)
O pwE segue os mais altos padrões de arquitetura de software de mercado para as suas conexões locais e na nuvem. A seguir, explore a documentação interativa e orientada a código para os principais endpoints e canais de plataforma do ecossistema.
Envia o texto da Escritura Sagrada (seja via digitação direta, liturgia diária ou capturado localmente por OCR) e o estado afetivo-espiritual do fiel para o orientador espiritual de IA na nuvem. A resposta é formulada estritamente sob as diretrizes do Magistério e do Fading Scaffolding de maturidade.
Parâmetros do Body (JSON)
| Parâmetro | Tipo | Descrição |
|---|---|---|
| passage* obrigatório | string | O texto bíblico completo do versículo ou capítulo selecionado para a meditação. |
| emotion | string | Disposição emocional do fiel (ex: "ansioso", "grato", "tribulado") para contextualizar a resposta pastoral. |
| maturity_level* obrigatório | string | Nível de progresso no app que define a verbosidade e profundidade (beginner, practitioner, contemplative). |
import 'package:pwe/core/network/api_client.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_request.dart';
import 'package:pwe/features/lectio_divina/data/models/meditation_response.dart';
final apiClient = GetIt.I<ApiClient>();
final request = MeditationRequest(
passage: "O Senhor é o meu pastor, nada me faltará. Em verdes pastagens me faz repousar. (Salmo 22, 1-2)",
emotion: "ansioso",
maturityLevel: MaturityLevel.beginner,
);
final Either<Failure, MeditationResponse> result =
await apiClient.post('/v1/spiritual-advisor/meditate', data: request.toJson());
result.fold(
(failure) => print('Erro espiritual/rede: ${failure.message}'),
(meditation) => print('Meditação gerada: ${meditation.guidance}'),
);
curl -X POST "https://api.pwe.app/v1/spiritual-advisor/meditate" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer SEU_TOKEN_AQUI" \
-d '{
"passage": "O Senhor é o meu pastor, nada me faltará. (Salmo 22, 1)",
"emotion": "ansioso",
"maturity_level": "beginner"
}'
{
"passage": "O Senhor é o meu pastor, nada me faltará. Em verdes pastagens me faz repousar. Conduz-me junto às águas refrescantes, restaura as forças de minha alma. (Salmo 22, 1-3)",
"emotion": "ansioso",
"maturity_level": "beginner"
}
{
"status": "success",
"data": {
"passage_reference": "Salmo 22, 1-3",
"maturity_applied": "beginner",
"spiritual_guidance": "Meu filho em Cristo, a ansiedade tenta turvar as águas da tua alma, mas o Bom Pastor te convida ao repouso no Seu amor. Diante da agitação do mundo, lembra-te de que Ele tem o controle absoluto de todas as coisas. Entrega-Lhe as tuas preocupações no silêncio da oração. Nas palavras de São Padre Pio: 'Reze, espere e não se preocupe. A preocupação é inútil. Deus é misericordioso e ouvirá sua oração.' Que o teu coração descanse hoje na certeza da Providência Divina.",
"reflection_points": [
"Como você tem buscado as 'verdes pastagens' da oração no meio das tribulações cotidianas?",
"De que maneira a entrega confiante ao Bom Pastor pode acalmar a tempestade da sua ansiedade hoje?",
"Faça um ato de fé simples: 'Jesus, eu confio em Vós, mesmo quando não vejo o caminho.'"
]
}
}
Consome a API oficial para carregar os textos litúrgicos da Igreja Católica Apostólica Romana válidos para a data corrente ou uma data opcional. Retorna de forma estruturada a Primeira Leitura, Salmo Responsorial, Segunda Leitura (se houver) e o Evangelho para apoiar a Lectio Divina.
Parâmetros da Query (Request URL)
| Parâmetro | Tipo | Descrição |
|---|---|---|
| date | string | Data alvo no formato ISO-8601 YYYY-MM-DD. O padrão é a data atual do servidor. |
| translation | string | Tradução e versão bíblica desejada (ex: cnbb, ave_maria, jerusalem). Padrão: cnbb. |
import 'package:pwe/features/lectio_divina/domain/repositories/liturgy_repository.dart';
import 'package:pwe/features/lectio_divina/domain/entities/liturgical_day.dart';
final liturgyRepo = GetIt.I<LiturgyRepository>();
final Either<Failure, LiturgicalDay> result =
await liturgyRepo.getTodayLiturgy(translation: 'cnbb');
result.fold(
(failure) => print('Erro ao coletar liturgia: ${failure.message}'),
(liturgy) => print('Evangelho do dia (${liturgy.reference}): ${liturgy.content}'),
);
curl -X GET "https://api.pwe.app/v1/liturgy/today?translation=cnbb" \
-H "Accept: application/json"
{
"date": "2026-05-21",
"liturgical_season": "Tempo Pascal",
"liturgical_color": "Branco",
"celebration": "Quinta-feira da 7ª Semana da Páscoa",
"readings": {
"first_reading": {
"reference": "Atos 22, 30; 23, 6-11",
"title": "Paulo testemunha em Jerusalém e é enviado a Roma",
"content": "Naqueles dias, querendo saber com certeza de que os judeus o acusavam, o comandante soltou Paulo e mandou reunir os sumos sacerdotes e todo o conselho dos anciãos..."
},
"psalm": {
"reference": "Salmo 15 (16)",
"response": "Guardai-me, ó Deus, porque em vós me refugio!",
"verses": [
"Preservai-me, ó Deus, porque em vós me refugio! Eu digo ao Senhor: 'Vós sois o meu Deus'.",
"Ó Senhor, vós sois a minha herança e o meu cálice; nas vossas mãos está o meu destino."
]
},
"evangelho": {
"reference": "João 17, 20-26",
"title": "Que eles sejam um em nós, para que o mundo creia",
"content": "Naquele tempo, Jesus levantou os olhos ao céu e rezou, dizendo: 'Não rogo somente por eles, mas também por aqueles que vão creer em mim pela sua palavra...'"
}
}
}
Canal nativo de comunicação de plataforma (MethodChannel) para delegar de forma assíncrona o processamento de inferência do Large Language Model (Gemma 2B quantizado via LiteRT-LM) para execução 100% isolada e offline no smartphone do usuário. Garante soberania e privacidade inviolável.
Argumentos de Entrada (Method Call)
| Argumento | Tipo | Descrição |
|---|---|---|
| prompt* obrigatório | string | Prompt contendo a passagem e o contexto católico estrito a ser alimentado no Gemma local. |
| max_tokens | int | Limite de tokens a serem gerados pelo modelo. Padrão: 150. |
| temperature | double | Fator de aleatoriedade térmica do decodificador. Padrão: 0.3. |
import 'package:flutter/services.dart';
const MethodChannel _channel = MethodChannel('pwe.app/gemma-inference');
Future<String?> generateLocalGuidance({
required String prompt,
int maxTokens = 150,
double temperature = 0.3,
}) async {
try {
final String? result = await _channel.invokeMethod<String>(
'executeGemmaInference',
{
'prompt': prompt,
'max_tokens': maxTokens,
'temperature': temperature,
},
);
return result;
} on PlatformException catch (e) {
print("Erro na inferência local Gemma: ${e.message}");
return null;
}
}
package com.pwe.app.inference
import android.content.Context
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import com.google.mediapipe.tasks.genai.llminference.LlmInference
class GemmaInferencePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {
private lateinit var channel: MethodChannel
private var llmInference: LlmInference? = null
override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {
channel = MethodChannel(binding.binaryMessenger, "pwe.app/gemma-inference")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method == "executeGemmaInference") {
val prompt = call.argument<String>("prompt") ?: ""
val maxTokens = call.argument<Int>("max_tokens") ?: 150
val temp = call.argument<Double>("temperature") ?: 0.3
try {
// Inferência executada localmente de forma isolada
val response = llmInference?.generateResponse(prompt)
result.success(response)
} catch (e: Exception) {
result.error("GENAI_ERROR", e.localizedMessage, null)
}
} else {
result.notImplemented()
}
}
}
{
"prompt": "Contexto: Atue como um orientador espiritual católico fiel ao Magistério da Igreja. Passagem: João 17, 20-26. Gere 2 pontos curtos de meditação.",
"max_tokens": 150,
"temperature": 0.3
}
{
"status": "success",
"engine": "LiteRT-LM (Gemma 2B)",
"inference_time_ms": 342,
"result": "1. Unidade na Caridade: Jesus roga ao Pai por nossa unidade como sinal visível da Sua missão divina. Rezemos pela coesão da Igreja hoje.\n2. O Amor Divino em Nós: A oração de Cristo pede que o amor com que o Pai O amou habite em nós. Deixemo-nos possuir por esse amor no silêncio da Contemplatio."
}
Decisões de Arquitetura (ADRs)
O pwE mantém registros históricos das principais decisões de arquitetura de software (localizados na pasta docs/adr/), garantindo rastreabilidade técnica.
Status: Aceito | Objetivo: Separar a lógica do negócio (pureza do domínio) de agentes de infraestrutura.
Decidiu-se que a camada de Domain não possui qualquer importação do framework Flutter ou Firebase. Qualquer comunicação externa é definida via contratos de interface e resolvida dinamicamente no container do GetIt.
A separação de responsabilidades permite que toda a inteligência do aplicativo seja exaustivamente testada em ambiente de console simples, reduzindo o tempo de build em CI/CD.
Status: Aceito | Objetivo: Garantir estabilidade e velocidade na execução local do Gemma 4.
Foram avaliados pacotes prontos como `flutter_gemma` e `mediapipe_genai`. Contudo, devido a relatos de OOM (Out of Memory) e falta de suporte para threads em segundo plano no iOS/Android, optamos por criar canais nativos de plataforma (**MethodChannel** no Android com Kotlin utilizando a API LiteRT-LM nativa).
Isso permite usar aceleração física por GPU/NPU real dos chips modernos com total estabilidade.
Status: Proposto | Objetivo: indexação 100% offline e livre de vazamento de dados de orações.
O RAG precisa vetorializar as anotações do diário pessoal do usuário para busca semântica de longo prazo. Fazer isso na nuvem geraria vazamento de intimidade de fé.
A decisão foi utilizar o **MobileBERT** quantizado no dispositivo via LiteRT, permitindo gerar embeddings de 384 dimensões em milissegundos e sem conexão de rede.
Status: Aceito | Objetivo: Eliminar acoplamento excessivo e concorrência de UI no BLoC central.
O LectioBloc monolítico foi decomposto em 6 sub-BLoCs com responsabilidades atômicas e imutáveis: LectioSessionBloc, DailyLiturgyBloc, ExegesisBloc, PrayerJournalBloc, AiOratioBloc e SubscriptionBloc. Essa segregação elevou a testabilidade e viabilizou mais de 930 testes automatizados.
Status: Aceito | Objetivo: Garantir soberania espiritual e conformidade com privacidade por design.
Qualquer chamada a modelos LLM remotos exige consentimento prévio e transparente do usuário via diálogo dedicado. O app oferece alternativa clara para a prática da Lectio Divina puramente contemplativa sem qualquer IA ou uso de IA 100% on-device (Escudo Dourado).
Status: Aceito | Objetivo: Blindagem total das orações íntimas e reflexões do Caderno Espiritual.
Todas as caixas de persistência Hive contendo anotações da Lectio Divina são criptografadas em repouso com algoritmo AES-256-GCM, vetor de inicialização (IV) aleatório e chave derivada por PBKDF2 ancorada no Android Keystore e Apple Keychain.
Status: Aceito | Objetivo: Higiene arquitetural e bloqueio de use cases de simulação em produção.
A feature de cofre de segurança teve seu contêiner de injeção de dependência purgado do core e isolado em security_vault_injection_container.dart. Use cases de teste e bypass foram blindados sob if (!kDebugMode) para evitar brechas em builds de release.
Status: Aceito | Objetivo: Respeitar a pedagogia da Lectio Divina e adequar telas ao Design System Sacro.
A seleção de motor de IA foi desacoplada do cold start e reposicionada de forma orgânica ao término do onboarding (FTUE), adotando o Design System Sacro (Pergaminho Nobre / Shalom), tipografia Cinzel/Lora e divisores com cruz.
Status: Aceito | Objetivo: Estruturar governança técnica autônoma e concorrente para 16 especialistas.
Formalização da arquitetura multiagente com subagentes assíncronos declarados em .agents/agents/, com esquemas YAML padronizados, isolamento de workspaces e ferramentas especializadas, operando nas frentes de Engenharia, Teologia, FinOps, Design/A11y, QA e DevOps.
Histórico de Refatorações (Logs)
Acompanhe os logs técnicos de grandes evoluções e melhorias de engenharia aplicadas ao projeto (encontrados em docs/refactor/).
O download de um modelo de 1.3GB é crítico em redes móveis de internet. A refatoração implementou:
- Download Particionado: Uso de buffers estáveis para evitar OOM durante a escrita local.
- Validação de Integridade: Cálculo de hash SHA256 pós-download antes do descompactamento.
- GlobalDownloadTracker: Um widget que flutua acima de todo o aplicativo mostrando o percentual do progresso, permitindo que o usuário navegue ou reze enquanto o download é concluído em background.
O processador cru do ML Kit capturava ruídos gráficos das bordas e sombras das páginas físicas da Bíblia. A refatoração introduziu:
- Binarização por Limiar Adaptativo: Conversão rápida da foto capturada para preto e branco de alto contraste via Canvas nativo antes de submetê-la ao OCR.
- Algoritmo de Correção Lexical: Um micro-parser local que cruza o texto capturado com expressões litúrgicas comuns para corrigir falhas sutis de reconhecimento de caracteres.
Resolução de Problemas (Troubleshooting)
Seção interativa destinada a engenheiros e testadores para resolver rapidamente problemas comuns de ambiente e build no projeto pwE.
Ocorrem erros quando o Gradle Wrapper ou o Gradle Plugin do Android não coincidem com o nível exigido de SDK. Para atualizar para uma versão estável e recomendada:
# Navegue até o diretório android do projeto
cd android
# Atualize o Gradle Wrapper para a versão estável compatível (ex: 8.13)
./gradlew wrapper --gradle-version 8.13
Certifique-se também de que o arquivo gradle-wrapper.properties possua o link de distribuição devidamente configurado para a versão estável em HTTPS.
Para garantir que o arquivo binário do modelo `.litertlm` (com ~1.3GB) não foi corrompido durante o download opt-in do usuário, o app valida o hash SHA256.
Caso o carregamento falhe continuamente, confirme a assinatura configurada no seu painel do Firebase Remote Config:
# Variáveis esperadas no Remote Config
enable_gemma_local: true
gemma_model_sha256: "COLOCAR_HASH_SHA256_REAL_DO_MODELO_AQUI"
gemma_model_download_url: "https://seu-cdn-seguro.com/models/gemma-2b-it.litertlm"
Se o hash diferir, o `GemmaLocalDataSource` rejeitará a inicialização local e acionará imediatamente o fallback para o Gemini na nuvem por segurança.
Ao rodar testes unitários e de integração de estado no BLoC (ex: `lectio_bloc_test.dart`), certifique-se de injetar os usecases mockados via **Mockito** ou **Mocktail** e registrar no GetIt de testes para evitar a chamada a APIs reais de rede.
# Execute todos os testes unitários do projeto de forma limpa
flutter test --coverage
Diagrama de Contexto e Container (C4 Model)
Este diagrama do modelo C4 detalha as integrações de alto nível do aplicativo pwE com serviços locais e externos da nuvem.
graph TB
User((Usuário Católico))
pwE[App pwE - Mobile]
subgraph External_Systems [Sistemas Externos]
Firebase[Firebase: Auth, Firestore, Crashlytics]
LLM[IA: OpenAI / Gemini / Gemma]
BibleAPI[API de Bíblia / Liturgia Diária]
MLKit[Google ML Kit - OCR Local]
end
User -->|Pratica Lectio Divina| pwE
pwE -->|Autentica e Sincroniza| Firebase
pwE -->|Solicita Meditações/Orações| LLM
pwE -->|Busca Passagens e Liturgia| BibleAPI
pwE -->|Processa Imagens Offline| MLKit
Diagrama de Sequência da Sessão
Visualize o ciclo de vida completo de uma sessão de oração, acompanhando o tráfego de dados e comandos pelas camadas do software.
sequenceDiagram
autonumber
participant U as Usuário
participant UI as Presentation (Bloc/UI)
participant D as Domain (UseCases)
participant R as Data (Repositories/DS)
participant IA as IA (Gemini/Gemma)
participant B as DB (Firestore/Hive)
U->>UI: Inicia Sessão (Informa Emoção)
UI->>D: SuggestPassageUseCase(emotion)
D->>R: IARepository.getSuggestion()
R->>IA: Prompt (Spiritual Advisor Context)
IA-->>R: Sugestão de Passagem + Motivo
R-->>D: DailyPassage Suggestion
D-->>UI: LectioInProgress(suggestedPassage)
U->>UI: Lê e Inicia momento Lectio
UI->>U: Exibe Texto da Escritura
U->>UI: Avança para Meditatio
UI->>D: GetGuidedMeditationUseCase(passage)
D->>R: IARepository.getMeditationPoints()
R->>IA: Prompt (Meditatio alinhada ao Magistério)
IA-->>R: Pontos de Meditação Concreta
R-->>D: MeditationPoints
D-->>UI: LectioInProgress(guidingPrompt)
U->>UI: Avança para Oratio & Escreve preces
UI->>D: GetPrayerPromptUseCase()
D->>R: IARepository.getPrayerInspiration()
R->>IA: Prompt baseado na Tradição Católica
IA-->>R: Inspiração de Oração
R-->>D: PrayerPrompt
D-->>UI: LectioInProgress(prayerPrompt)
U->>UI: Escreve resolução (Contemplatio)
U->>UI: Conclui Sessão (HoldToSaveButton)
UI->>D: SaveSessionUseCase(session)
D->>R: SessionRepository.save()
R->>B: Persiste localmente e na nuvem
B-->>R: Sucesso
R-->>D: Sucesso
D-->>UI: LectioCompleted
UI->>U: Exibe Ecrã de Celebração de Streak
Máquina de Estados: LectioBloc
Gerenciamento rigoroso de estados e fluxos de tela assíncronos no pwE utilizando a tríade Evento, Estado e BLoC.
stateDiagram-v2
[*] --> LectioInitial: Inicialização da Feature
LectioInitial --> LectioInProgress_Lectio: StartSessionEvent
state LectioInProgress {
[*] --> LectioInProgress_Lectio
LectioInProgress_Lectio --> LectioInProgress_Meditatio: AdvanceToStep(1)
LectioInProgress_Meditatio --> LectioInProgress_Oratio: AdvanceToStep(2)
LectioInProgress_Oratio --> LectioInProgress_Contemplatio: AdvanceToStep(3)
LectioInProgress_Contemplatio --> [*]: CompleteSessionEvent
}
LectioInProgress --> LectioCompleted: Session Saved Successfully
LectioInProgress --> LectioError: Failure (API/Network/DB)
LectioError --> LectioInitial: Reset / Tentar Novamente
LectioCompleted --> [*]
Estrutura de Pastas e Mapeamento Clean Architecture
A árvore de diretórios do repositório pwE reflete a intenção das camadas e o isolamento de domínios.
Árvore de Pastas
lib/
├── core/ # Núcleo compartilhado
│ ├── di/ # Container GetIt (injection_container.dart)
│ ├── error/ # Failures e Exceptions (Either)
│ ├── network/ # REST Clients e configurações de rede
│ ├── routes/ # Configurações de rotas com GoRouter
│ └── theme/ # Estilos visuais e paletas do aplicativo
├── features/ # Módulos de domínio isolados
│ ├── lectio_divina/ # Domínio principal de Leitura Orante
│ │ ├── data/ # DTOs, Repositórios e fontes de dados
│ │ ├── domain/ # Entidades, regras de negócios e UseCases
│ │ └── presentation/ # Controladores BLoC, widgets e páginas
│ ├── auth/ # Gerenciamento de login e segurança
│ └── profile/ # Controle de maturidade e preferências
└── main.dart # Configurações e bootstrap do app