critical priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

EmailChanged event handler updates email field in LoginFormIdleState without triggering loading state
PasswordChanged event handler updates password field in LoginFormIdleState without triggering loading state
Emitting EmailChanged with empty string sets emailError to 'email_empty' key in idle state
Emitting EmailChanged with invalid format (e.g. 'notanemail') sets emailError to 'email_invalid_format' key
Emitting EmailChanged with valid email clears emailError field (null)
Emitting PasswordChanged with empty string sets passwordError to 'password_empty' key in idle state
Emitting PasswordChanged with non-empty password clears passwordError field (null)
SubmitPressed invokes CredentialValidator.validate() before any AuthService call
SubmitPressed with validation errors emits idle state with field errors, does NOT emit LoginLoading
SubmitPressed with valid credentials dispatches to AuthService and emits LoginLoading
Field error keys reference the error message registry (string constants, not raw strings)
BLoC state is equatable; repeated same-value events do not cause unnecessary rebuilds
All event handlers are registered in the BLoC constructor via on<EventType>()

Technical Requirements

frameworks
Flutter
BLoC (flutter_bloc)
data models
LoginFormState (LoginFormIdleState with emailError, passwordError, email, password fields)
LoginFormEvent (EmailChanged, PasswordChanged, SubmitPressed)
CredentialValidator
performance requirements
Field update handlers must complete synchronously — no async work in EmailChanged/PasswordChanged
No debounce on field handlers; validation fires immediately on every change
State equality check (Equatable) prevents redundant widget rebuilds
security requirements
Password field value must never be logged or printed — strip from debug output
Email trimmed and lowercased before validation to prevent case-sensitive auth failures

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Keep LoginFormIdleState immutable and use copyWith() for field updates. Error keys should be string constants defined in an ErrorMessageKeys or similar constants file — never inline string literals. The CredentialValidator should be injected via constructor for testability. EmailChanged/PasswordChanged handlers use emit() synchronously.

SubmitPressed calls CredentialValidator first; only if valid does it proceed to the async AuthService dispatch (handled in task-008). Separate the validation logic cleanly: task-007 owns the idle-state validation path, task-008 owns the loading/success/error path. Use Equatable on all state classes with props including email, password, emailError, passwordError.

Testing Requirements

Unit tests using bloc_test package. Test each event type in isolation: (1) EmailChanged with empty, invalid, and valid values — assert correct idle state emitted each time; (2) PasswordChanged with empty and non-empty values; (3) SubmitPressed with invalid fields — assert no LoginLoading emitted; (4) SubmitPressed with valid fields — assert LoginLoading is the next emitted state. Mock CredentialValidator and AuthService. Verify CredentialValidator.validate() is called exactly once on SubmitPressed.

Aim for 100% branch coverage on all three handlers.

Component
Login Form BLoC
service medium
Epic Risks (2)
high impact medium prob integration

Supabase GoTrue returns HTTP error codes and string messages that may change between SDK versions. Incorrect or incomplete mapping could cause the wrong user-facing message to be shown (e.g., showing a generic error instead of a specific credential error), violating the plain-language feedback acceptance criteria and potentially exposing security-sensitive information.

Mitigation & Contingency

Mitigation: Pin the supabase_flutter SDK to a specific minor version in pubspec.yaml. Write integration tests that mock the Supabase HTTP layer and assert each error code maps to the correct domain exception. Document the mapping table as a constant in AuthService.

Contingency: If an unrecognized error code is received at runtime, catch it as an UnknownAuthException and display a generic safe message. Alert via crash reporting for triage and SDK update.

medium impact medium prob technical

If the user taps the sign-in button multiple times rapidly, concurrent authentication requests could result in race conditions: duplicate network calls, out-of-order state emissions, or multiple session tokens being written to secure storage.

Mitigation & Contingency

Mitigation: Use bloc concurrency transformer (droppable or restartable) to ensure only one authentication event is processed at a time. The BLoC should guard against submission while in LoginLoading state.

Contingency: Add a UI-level disable on the submit button when loading state is active as a secondary guard independent of BLoC concurrency control.