Fluxo de Atualização de Telefone e E-mail
Escopo: Atualização de dados de contato do usuário no app Flutter (Coezzion Vendas)
Data: 2026-07-06
1. Resumo
Os fluxos de atualização de telefone e e-mail permitem que o usuário cadastre ou altere seus dados de contato com verificação por código de 6 dígitos. Ambos seguem a mesma arquitetura em três etapas:
- Informar o novo telefone ou e-mail
- Receber e validar o código de verificação enviado
- Persistir a alteração via API (
change-cellphone ou change-email)
O fluxo é acionado em três contextos distintos: obrigatoriamente após o login, pelo perfil do vendedor (usuário logado) ou como sub-fluxo da recuperação de senha. A diferença entre os contextos está principalmente na forma de autenticação das requisições HTTP.
2. Pontos de Entrada
Existem três contextos que utilizam as mesmas telas e endpoints, com parâmetros diferentes:
| Contexto | Condição de ativação | customAuthorizationToken | canValidateByEmail | Arquivo |
|---|
| Pós-login obrigatório | is_update_contact_enabled == true e !user.isContactUpdated | authApp.auth.token | true | lib/screens/login/login_manager.dart |
| Perfil do vendedor | Usuário logado, ícone de edição em dados de contato | null (usa AuthInterceptor) | false (telefone) | lib/screens/seller_profile/seller_profile/widget/seller_contact_details.dart |
| Recuperação de senha | Limite de SMS excedido ou troca de método em CodeVerificationByMethodScreen | null | true | lib/controllers/code_verification/by_method/code_verification_by_method_controller.dart |
Definida em lib/models/user/user.dart:
bool get isContactUpdated {
if (cellPhoneConfirmed) return true;
if (emailConfirmed) return true;
return false;
}
O fluxo pós-login só é exibido quando nenhum dos dois contatos está confirmado.
- Login bem-sucedido via
tryCompletLogin
- Verifica Remote Config
is_update_contact_enabled
- Se contato não atualizado, fecha loader e exibe bottom sheet obrigatório
- Navega para
UpdateUserContactScreen passando authApp completo
- Se usuário cancela (
result == null), aborta o login
- Se atualiza com sucesso, retorna
authApp atualizado e continua o fluxo de login
3. Fluxo de Telefone
Passo a passo
| Etapa | Tela | Ação | API |
|---|
| 1 | UpdatePhoneScreen | Informa telefone e toca Continuar | — |
| 2 | CodeVerificationByPhoneScreen | Código enviado automaticamente ao abrir | POST /api/auth/verify-phone |
| 3 | CodeVerificationByPhoneScreen | Informa código de 6 dígitos | POST /api/auth/verify-code |
| 4 | (interno ao controller) | Persiste alteração | POST /api/auth/change-cellphone |
| 5 | Tela anterior | Retorna resultado e atualiza estado local | — |
4. Fluxo de E-mail
Passo a passo
| Etapa | Tela | Ação | API |
|---|
| 1 | UpdateEmailScreen | Informa e-mail e toca Continuar | — |
| 2 | CodeVerificationByEmailScreen | Código enviado automaticamente ao abrir | POST /api/auth/verify-email |
| 3 | CodeVerificationByEmailScreen | Informa código de 6 dígitos | POST /api/auth/verify-code |
| 4 | (interno ao controller) | Persiste alteração | POST /api/auth/change-email |
| 5 | Tela anterior | Retorna resultado e atualiza estado local | — |
5. Endpoints
Base URL dinâmica: {baseUrl} via PrefsService.getEnvConfig().
5.1 Fluxo de telefone
| # | Método | Endpoint | Controller | Request Body |
|---|
| 1 | POST | /api/auth/verify-phone | CodeVerificationByPhoneController.sendCode | {cpf, cellPhone} |
| 2 | POST | /api/auth/verify-code | CodeVerificationByPhoneController.verifyCode | {cpf, cellPhone, VerificationCode} |
| 3 | POST | /api/auth/change-cellphone | CodeVerificationByPhoneController.updatePhone | {cpf, cellPhone, token} |
Payloads:
{ "cpf": "12345678900", "cellPhone": "11999998888" }
{ "cpf": "12345678900", "cellPhone": "11999998888", "VerificationCode": "123456" }
{ "cpf": "12345678900", "cellPhone": "11999998888", "token": "<token>" }
5.2 Fluxo de e-mail
| # | Método | Endpoint | Controller | Request Body |
|---|
| 1 | POST | /api/auth/verify-email | CodeVerificationByEmailScreenController.sendCode | {cpf, email} |
| 2 | POST | /api/auth/verify-code | CodeVerificationByEmailScreenController.verifyCode | {cpf, email, VerificationCode} |
| 3 | POST | /api/auth/change-email | CodeVerificationByEmailScreenController.updateEmail | {cpf, email, token} |
Payloads:
{ "cpf": "12345678900", "email": "usuario@email.com" }
{ "cpf": "12345678900", "email": "usuario@email.com", "VerificationCode": "123456" }
{ "cpf": "12345678900", "email": "usuario@email.com", "token": "<token>" }
5.3 Constantes em api_utils.dart
static const forgetPasswordSendCode = '$authApi/verify-phone';
static const forgetEmailSendCode = '$authApi/verify-email';
static const forgetPasswordValidateCode = '$authApi/verify-code';
static const updatePhone = '$api/auth/change-cellphone';
static const updateEmail = '$api/auth/change-email';
Nota: Os endpoints verify-phone, verify-email e verify-code compartilham nomenclatura com o fluxo de recuperação de senha, mas utilizam payloads diferentes do fluxo verify-recover-method / verify-code-recover documentado em recover.md.
6. Telas e Navegação
6.1 Telas principais
Rota (AppRoutes) | Tela | Título | Controller |
|---|
/update_user_contact | UpdateUserContactScreen | Atualizar telefone ou e-mail | UpdateUserContactController |
/update_phone | UpdatePhoneScreen | Atualizar telefone | UpdatePhoneController |
/update_email | UpdateEmailScreen | Atualizar e-mail | UpdateEmailController |
/code_verification_by_phone | CodeVerificationByPhoneScreen | Código de verificação | CodeVerificationByPhoneController |
/code_verification_by_email | CodeVerificationByEmailScreen | Código de verificação | CodeVerificationByEmailScreenController |
6.2 Argumentos de navegação
| Classe | Campos | Uso |
|---|
UpdateUserContactScreenArgument | authApp | Hub pós-login com objeto de autenticação completo |
UpdatePhoneScreenArgument | cpf, customAuthorizationToken, canValidateByEmail | Entrada da tela de telefone |
UpdateEmailScreenArgument | cpf, customAuthorizationToken | Entrada da tela de e-mail |
CodeVerificationByPhoneArguments | cpf, cellPhone, customAuthorizationToken, canRedirectToEmail | Verificação SMS |
CodeVerificationByEmailScreenArguments | cpf, email, customAuthorizationToken | Verificação e-mail |
6.3 Resultados de navegação
| Classe | Campos | Retornado por |
|---|
UpdateUserContactResult | contactUpdated, updatedAuthApp | UpdateUserContactController → LoginManager |
UpdatePhoneScreenResult | phone | UpdatePhoneController |
UpdateEmailScreenResult | email, token, expirationDate | UpdateEmailController |
CodeVerificationByPhoneResult | isUpdated, token, expirationDate | CodeVerificationByPhoneController |
CodeVerificationByEmailScreenResult | isUpdated, token, expirationDate | CodeVerificationByEmailScreenController |
ValidateByEmailResult | — | CodeVerificationByPhoneController (fallback SMS → e-mail) |
Tela intermediária que exibe sempre as duas opções (Telefone e E-mail). Ao concluir qualquer uma:
- Atualiza
authApp.user.cellPhone / authApp.user.email
- Define
cellPhoneConfirmed ou emailConfirmed = true
- Retorna
UpdateUserContactResult para o LoginManager
Se o usuário falha no SMS e aceita validar por e-mail, UpdateUserContactController.updatePhone redireciona automaticamente para updateEmail.
7. Keys e Autenticação
7.1 Firebase Remote Config
| Key | Tipo | Propriedade Dart | Uso |
|---|
is_update_contact_enabled | bool | isUpdateContactEnabled | Habilita fluxo obrigatório pós-login |
recover_methods_token | string | recoverMethodsToken | Header x-api-key para todos os endpoints de verificação e alteração de contato |
| Header | Quando aplicado | Valor |
|---|
x-api-key | verify-phone, verify-email, verify-code, change-cellphone, change-email | recover_methods_token via VerifyMethodsInterceptor |
Authorization: Bearer {token} | Fluxo pós-login (antes de setAuthApp) | customAuthorizationToken passado manualmente em DefaultDioProvider.post |
Authorization | Perfil do vendedor (usuário logado) | Injetado automaticamente por AuthInterceptor |
7.3 Diferença de autenticação por contexto
8. Modelos Request/Response
Requests
| Modelo | Campos | Endpoint |
|---|
SendValidationCodeRequest (byPhone) | cpf, cellPhone | verify-phone |
SendValidationCodeRequest (byEmail) | cpf, email | verify-email |
ValidateCodeRequest (byPhone) | cpf, cellPhone, VerificationCode | verify-code |
ValidateCodeRequest (byEmail) | cpf, email, VerificationCode | verify-code |
UpdatePhoneRequest | cpf, cellPhone, token | change-cellphone |
UpdateEmailRequest | cpf, email, token | change-email |
Responses
| Modelo | Campos relevantes |
|---|
SendValidationCodeResponse | isSuccess, error.message |
ValidateCodeResponse | isSuccess, success.token, success.expirationDate, error.message |
UpdatePhoneResponse | isSuccess, success/error (title, message, needConfirm) |
UpdateEmailResponse | isSuccess, success/error (title, message, needConfirm) |
9. Regras de Negócio
Validação de entrada
| Campo | Regra | Implementação |
|---|
| Telefone | Formato brasileiro válido | flutter_libphonenumber com região BR; botão habilitado com 15 caracteres mascarados |
| E-mail | Formato válido | text.isEmail (extensão GetX) |
Timer de reenvio
- Intervalo de 2 minutos entre solicitações
PhoneCodeVerificationTimerController para SMS
EmailCodeVerificationTimerController para e-mail
- Botão "Reenviar código" habilitado apenas após expiração
Limite de tentativas SMS
Implementado em PhoneCodeVerificationCounterService:
- Máximo de 3 tentativas em 1 hora (0 em modo debug)
- Após exceder, exibe bottom sheet oferecendo validação por e-mail
- Só se aplica quando
canValidateByEmail == true (pós-login e recuperação de senha)
- No perfil do vendedor (
canValidateByEmail: false), o fallback para e-mail não está disponível
Comportamento pós-sucesso por contexto
| Contexto | Ação após sucesso |
|---|
| Pós-login | Atualiza authApp in-memory, define cellPhoneConfirmed/emailConfirmed = true, retorna UpdateUserContactResult |
| Perfil vendedor | AppEssentialDataService.updateSellerPhone/Email + EditedUserInformationRewardEvent |
| Recuperação de senha | Reinicia fluxo em ForgotPasswordScreen (Get.offNamedUntil) |
Cancelamento pós-login
Se o usuário sai do fluxo sem atualizar contato (result == null), o LoginManager retorna null em _updateContact e aborta o login — o usuário não prossegue para a home.
10. Diagramas
10.1 Mapa de pontos de entrada
10.2 Sequência API — telefone
10.3 Sequência API — e-mail
10.4 Fallback SMS para e-mail
10.5 Fluxo pós-login completo
Os fluxos de atualização de contato compartilham telas e endpoints com o fluxo de recuperação de senha documentado em docs/recover.md, mas com propósitos distintos:
| Aspecto | update-contact (este doc) | recover.md |
|---|
| Objetivo | Atualizar telefone/e-mail do usuário | Redefinir senha de acesso |
| Endpoints de verificação | verify-phone, verify-email, verify-code | verify-recover-method, verify-code-recover |
| Endpoint final | change-cellphone / change-email | change-password |
| Hub de seleção | UpdateUserContactScreen (sempre 2 opções) | UpdateUserContactByMethodsScreen (métodos da API) |
| Autenticação | Pode usar Authorization (pós-login) ou apenas x-api-key | Apenas x-api-key (sem login) |
Sub-fluxo compartilhado: durante a recuperação de senha, quando o usuário excede o limite de SMS, CodeVerificationByMethodScreenController.tryAnotherMethod aciona UpdatePhoneScreen ou UpdateEmailScreen — os mesmos fluxos documentados aqui. Após atualizar o contato, o usuário é redirecionado de volta ao início da recuperação de senha.
12. Arquivos Relacionados
Controllers
| Arquivo | Responsabilidade |
|---|
lib/controllers/update_user_contact/update_user_contact_controller.dart | Hub pós-login, orquestra telefone/e-mail |
lib/controllers/update_phone/update_phone_controller.dart | Validação e navegação para verificação SMS |
lib/controllers/update_email/update_email_controller.dart | Validação e navegação para verificação e-mail |
lib/controllers/code_verification/phone/code_verification_by_phone_controller.dart | Envio/validação SMS + change-cellphone |
lib/controllers/code_verification/email/code_verification_by_email_controller.dart | Envio/validação e-mail + change-email |
lib/controllers/seller/seller_edit_controller.dart | Entrada de edição de e-mail no perfil |
Screens
| Arquivo | Rota |
|---|
lib/screens/update_user_contact/update_user_contact_screen.dart | /update_user_contact |
lib/screens/update_phone/update_phone_screen.dart | /update_phone |
lib/screens/update_email/update_email_screen.dart | /update_email |
lib/screens/code_verification/phone/code_verification_by_phone_screen.dart | /code_verification_by_phone |
lib/screens/code_verification/email/code_verification_by_email_screen.dart | /code_verification_by_email |
lib/screens/seller_profile/seller_profile/widget/seller_contact_details.dart | Entrada perfil vendedor |
lib/screens/login/login_manager.dart | Entrada pós-login |
Providers
| Arquivo | URL base |
|---|
lib/providers/update_phone/update_phone_provider.dart | change-cellphone |
lib/providers/update_email/update_email_provider.dart | change-email |
lib/providers/code_verification/phone/code_verification_by_phone_provider.dart | URL dinâmica |
lib/providers/code_verification/email/code_verification_by_email_provider.dart | URL dinâmica |
Models
| Arquivo | Descrição |
|---|
lib/models/update_phone/update_phone_request.dart | Request change-cellphone |
lib/models/update_phone/update_phone_response.dart | Resposta change-cellphone |
lib/models/update_email/update_email_request.dart | Request change-email |
lib/models/update_email/update_email_response.dart | Resposta change-email |
lib/controllers/forgot_password/send_validate_code_request.dart | Request envio de código |
lib/controllers/forgot_password/send_validation_code_response.dart | Resposta envio de código |
lib/models/code_verification/validate_code_request.dart | Request validação de código |
lib/models/code_verification/validate_code_response.dart | Resposta validação de código |
lib/models/user/user.dart | isContactUpdated, flags de confirmação |
Serviços e infraestrutura
| Arquivo | Descrição |
|---|
lib/services/app_essential_data_service.dart | updateSellerPhone, updateSellerEmail |
lib/services/firebase_remote_config_service.dart | isUpdateContactEnabled, recoverMethodsToken |
lib/middlewares/http/recover_methods_interceptor.dart | Injeta x-api-key |
lib/shared/utils/api_utils.dart | Constantes de endpoints |
lib/shared/routes/app_routes.dart | Constantes de rotas |
lib/shared/routes/app_pages.dart | Registro de páginas GetX |
lib/screens/code_verification/by_method/code_verification_counter_service.dart | Controle de tentativas SMS |