Plano de conformidade — ADR-0009 seleção e cópia de texto
Referência: ADR-0009, seção 8 — Seleção e cópia de texto
Regra: Texto somente leitura (labels, mensagens, conteúdo de cards, etc.) deve ser selecionável (SelectableText).
Exceções: (1) Textos puramente decorativos ou de um único caractere (ex.: iniciais em avatar) permanecem Text. (2) Textos de botões (labels de TextButton, FilledButton, OutlinedButton, ChoiceChip, itens de dropdown/menu clicável) permanecem Text, para não interferir na usabilidade do toque/clique (ADR-0009 atualizado).
Escopo da alteração
- Substituir
Text(...) por SelectableText(...) em todo conteúdo legível pelo usuário.
- Manter
Text apenas onde for exceção (decorativo / um caractere, ex. iniciais em avatar).
- SnackBars e diálogos: conteúdo de mensagem em
SelectableText; labels de botões (e elementos clicáveis como ChoiceChip, itens de dropdown) em Text para não interferir na usabilidade.
Camada afetada: apenas presentation (Flutter). Nenhuma alteração em domain, application ou infrastructure.
Ordem de execução
Sugestão: por módulo, da base (widgets compartilhados e shell) para páginas e diálogos, para poder reutilizar padrões.
| # | Módulo / arquivo | Itens | Prioridade |
|---|
| 1 | home_shell_widgets.dart | Abas, rodapé | Base |
| 2 | login_page.dart | Dialogs, SnackBars, labels, AppBar | Alta |
| 3 | register_page.dart | Dialogs, SnackBars, labels, listas de validação | Alta |
| 4 | home_page.dart | Dialog igreja, SnackBars, card principal, versão | Alta |
| 5 | home_profile_dialog.dart | Título "Perfil", label "Sair" | Média |
| 6 | home_profile_dialog_actions.dart | SnackBars, títulos de diálogo, mensagens, botões de opção | Média |
| 7 | home_profile_dialog_avatar.dart | Títulos e conteúdos de diálogos (troca igreja, motivo); manter Text só para iniciais | Média |
| 8 | home_profile_dialog_tabs.dart | Labels, "Cargo atual", solicitações, mensagens, dropdown tema | Média |
| 9 | avatar_viewer_dialog.dart | Texto "Zoom X.XXx"; manter Text para iniciais | Baixa |
| 10 | debug_help_popup.dart | Títulos, labels, sessão, documentação de ajuda | Baixa (debug) |
Detalhamento por arquivo
1. presentation/pages/home/widgets/home_shell_widgets.dart
| Local | Atual | Ação |
|---|
Labels das abas (_tabs[index]) | Text | SelectableText |
| Rodapé "Fiel a todo momento" | Text | SelectableText |
| "EBDbe v$version" | Text | SelectableText |
2. presentation/pages/login/login_page.dart
| Local | Atual | Ação |
|---|
| SnackBar "Bem-vindo...", erro, "Login com Google indisponível...", etc. | Text | SelectableText (conteúdo do SnackBar) |
| Dialog recuperar conta: título "Recuperar conta" | Text | SelectableText |
| Dialog: corpo "Informe seu email..." | Text | SelectableText |
| Botões "Cancelar", "Enviar link" | Text | SelectableText (ou manter se preferir só conteúdo longo selecionável) |
| Mensagens de sucesso/erro no dialog | Text | SelectableText |
| AppBar title "Entrar" | Text | SelectableText |
| "Esqueci minha senha" | Text | SelectableText |
| "Ainda não tem conta?" | Text | SelectableText |
| "Criar conta" (botão) | Text | SelectableText |
| "ou:" | Já SelectableText | Manter |
3. presentation/pages/login/register_page.dart
| Local | Atual | Ação |
|---|
| Lista de validação: "$title: $compactLine", "falta atender:", "• $item" | Text | SelectableText |
| Conteúdo de todos os dialogs (Firebase, "Conta autenticada...", "Preencha Nome e Igreja...", link verificação, "Já existe cadastro...", etc.) | Text | SelectableText |
| SnackBars (erro, sucesso, "Preencha...", "Reenviando...", etc.) | Text | SelectableText |
| "Tentar novamente", "Cadastrar com email", "Reenviar email de confirmacao" | Text | SelectableText |
| AppBar "Criar conta" | Text | SelectableText |
| Hint do campo (se for texto estático legível) | Avaliar | SelectableText se for mensagem longa |
| Já em SelectableText | — | Manter |
4. presentation/pages/home/home_page.dart
| Local | Atual | Ação |
|---|
| Dialog igreja: title "Selecione sua igreja" | Text | SelectableText |
| Dialog igreja: corpo longo | Text | SelectableText |
Lista: item.label | Text | SelectableText |
| Botões "Cancelar", "Concluir" | Text | SelectableText |
| Alert "Login indisponível..." | Text | SelectableText |
SnackBar _profileError | Text | SelectableText |
| Botão "Entrar" / "Entrar (indisponível)" | Text | SelectableText |
| Iniciais no avatar | Text(initial.toUpperCase()) | Manter Text (exceção) |
| SnackBar "Publicação: $fullLabel" | Text | SelectableText |
| "v$_appVersion" | Text | SelectableText |
| Card: "EBDbe", "EBD", "Escola Bíblica Dominical", "Igreja Batista Ebenézer...", "EBD aos domingos...", profileSubtitle, "Ações", "Conheça a Ebenézer" | Text | SelectableText |
5. presentation/modules/profile_dialog/home_profile_dialog.dart
| Local | Atual | Ação |
|---|
| Título "Perfil" | Text | SelectableText |
| Label "Sair" | Text | SelectableText |
6. presentation/modules/profile_dialog/home_profile_dialog_actions.dart
| Local | Atual | Ação |
|---|
| SnackBars ("Atualizado com sucesso.", error.message, "Falha ao processar...") | Text | SelectableText |
| Opções do bottom sheet: "Usar URL da foto", "Usar câmera", "Escolher da galeria" | Text | SelectableText |
| Dialog URL: title "URL da foto" | Text | SelectableText |
| Botões "Cancelar", "Usar foto" | Text | SelectableText |
| Mensagem "Não foi possível selecionar a imagem..." | Text | SelectableText |
7. presentation/modules/profile_dialog/home_profile_dialog_avatar.dart
| Local | Atual | Ação |
|---|
| Iniciais no avatar | Text(initials) | Manter Text (exceção) |
| Dialog troca igreja: title "Confirmar troca de igreja" | Text | SelectableText |
| Conteúdo "Você está trocando sua igreja..." | Text | SelectableText |
| Botões "Não", "Sim" | Text | SelectableText |
| Alert "Informe o motivo da solicitação de perfil." | Text | SelectableText |
8. presentation/modules/profile_dialog/home_profile_dialog_tabs.dart
| Local | Atual | Ação |
|---|
| Label de item (lista de igrejas/unidades) | Text(item.label) | SelectableText |
| Dialog troca igreja: title "Troca de igreja", conteúdo, "Entendi" | Text | SelectableText |
| Labels "Colar URL e salvar", "Galeria", "Câmera" | Text | SelectableText |
| "Cargo atual de operação: $currentRoleLabel" | Text | SelectableText |
| "Nenhum cargo aprovado ainda." | Text | SelectableText |
| Labels de role no dropdown/chip | Text(roleLabels[roleId] ?? roleId) | SelectableText |
| Parágrafo "Solicitar perfil acima do atual exige aprovação..." | Text | SelectableText |
| displayLabel em itens de lista | Text(item.displayLabel) | SelectableText |
| "Solicitações recentes" | Text | SelectableText |
| Título e subtítulo de solicitação (roleLabels, status, data) | Text | SelectableText |
| "Destaques (nível imediatamente abaixo)", "Demais mensagens agrupadas" | Text | SelectableText |
| message.title, message.body | Text | SelectableText |
| "Sem mensagens no momento." | Text | SelectableText |
| Dropdown tema: "Default", "Light", "Dark", "Sepia" | Text | SelectableText |
9. presentation/modules/profile_dialog/avatar_viewer_dialog.dart
| Local | Atual | Ação |
|---|
| Iniciais no placeholder | Text(widget.initials) | Manter Text (exceção) |
| "Zoom X.XXx" (valor dinâmico) | Text | SelectableText |
| Local | Atual | Ação |
|---|
| SnackBars | Text | SelectableText |
| "Nível de debug", labels de nível | Text | SelectableText |
| "Debug out", "Atualizar", "Limpar", "Copiar saída" | Text | SelectableText |
| "Comandos", "Limpar", "Injetar papéis:", "1. Catálogo...", "2. Visibilidade...", "Executar comando" | Text | SelectableText |
| "Atalhos 0–9", índices "0"-"9" | Text | SelectableText |
| "Sessão: ..." | Text | SelectableText |
| "Ajuda do aplicativo", bloco de documentação | Text | SelectableText |
| Saída de debug | Já SelectableText | Manter |
Regras ao implementar
- Semântica: usar
SelectableText com os mesmos parâmetros de estilo que o Text original (style, textAlign, maxLines, overflow quando fizer sentido).
- Const: onde hoje é
const Text('...'), usar const SelectableText('...') quando o conteúdo for literal.
- Interpolação: onde há
Text('... $var ...'), usar SelectableText('... $var ...') (sem const).
- SnackBar:
SnackBar(content: SelectableText(...)) — garantir que o conteúdo seja widget único ou que o primeiro filho seja o texto selecionável.
- Não alterar:
TextField, TextFormField, hint (é parte do input); apenas não bloquear cola neles (já é padrão).
- Exceções explícitas: manter
Text apenas para iniciais em avatar e textos de um caractere/decorativos.
Validação
- Após cada arquivo (ou lote):
fvm flutter analyze e checagem visual na tela.
- Ao final: percorrer cada tela e conferir que texto legível permite seleção/cópia (gesto longo ou Ctrl+C no web).
- Documentar no próprio ADR ou neste plano que a conformidade foi concluída (data e escopo).
Conclusão da execução
- Data: 2025-03-01
- Branch:
feat/adr-0009-selectable-text
- Escopo: Todos os 10 arquivos do plano alterados; texto legível passou a usar
SelectableText; exceções (iniciais em avatar) mantidas como Text.
- Validação:
fvm flutter analyze OK (apenas warning pré-existente em _hasAvatarDraft).
Estimativa
- Itens 1–4 (shell + login + register + home): ~40 substituições.
- Itens 5–8 (profile dialog): ~50 substituições.
- Itens 9–10 (viewer + debug): ~15 substituições.
Total aproximado: ~105 pontos de alteração. Pode ser feita em uma única branch (ex.: feat/adr-0009-selectable-text) ou em commits por arquivo/módulo.