Implement LoginFormBLoC field input and validation handlers
epic-email-password-login-auth-logic-task-007 — Implement event handlers for EmailChanged and PasswordChanged that update internal field state. On SubmitPressed, invoke CredentialValidator before dispatching to AuthService. Emit inline validation errors (empty field, invalid email format) as part of the idle state without triggering a loading state, keeping the UX non-blocking.
Acceptance Criteria
Technical Requirements
Execution Context
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.
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.
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.