high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

LoginForm reads LoginFormBLoC via BlocProvider.of<LoginFormBLoC>(context) — the BLoC is NOT instantiated inside LoginForm
Every keystroke in the email field dispatches an EmailChanged(value) event to LoginFormBLoC
Every keystroke in the password field dispatches a PasswordChanged(value) event to LoginFormBLoC
Tapping the submit button or activating the keyboard submit action dispatches a LoginSubmitted() event
Submit button is disabled (AppButton's enabled: false or onPressed: null) when BLoC state indicates the form is invalid
Submit button is disabled and shows a loading indicator when BLoC state is LoginFormLoading
Email and password AppTextField instances have enabled: false when BLoC state is LoginFormLoading, preventing input during auth request
Field-level error messages displayed in LoginForm come from BLoC validation state (e.g., state.emailError, state.passwordError) — client-side local validation from task-004 is superseded by BLoC state
BlocBuilder is used to rebuild only the portions of the form that depend on state (button, errors, loading) — not the entire form
On BLoC state LoginFormSuccess, the form does not navigate — navigation is the responsibility of LoginScreen (parent)

Technical Requirements

frameworks
Flutter
BLoC
flutter_bloc
performance requirements
Use BlocBuilder with buildWhen to limit rebuilds to state changes that affect the widget being built
Text controllers must not be recreated on every rebuild — initialise once in initState
security requirements
Do not store raw password in widget state beyond what is needed for the current text controller value
Clear text controllers on dispose to remove sensitive values from memory
ui components
BlocBuilder<LoginFormBLoC, LoginFormState>
AppTextField (email, wired to TextEditingController + EmailChanged event)
AppTextField (password, wired to TextEditingController + PasswordChanged event)
AppButton (enabled state driven by BLoC state)

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Refactor LoginForm from the previous tasks to remove local validation state — all validation state now comes from LoginFormBLoC. Keep local FocusNodes and TextEditingControllers (owned by LoginForm's State). In initState, attach listeners to text controllers that dispatch BLoC events via context.read().add(). Use BlocBuilder with targeted buildWhen predicates: one builder for submit button enabled state, one for field errors, one for loading state.

Avoid a single top-level BlocBuilder that rebuilds the whole form. For the loading state, disable both text fields and replace the button label with a SizedBox containing a small CircularProgressIndicator. Ensure TextEditingController.dispose() is called in State.dispose() alongside FocusNode.dispose().

Testing Requirements

Write bloc_test and widget tests. Bloc tests: verify EmailChanged event updates email validation state, PasswordChanged updates password state, LoginSubmitted triggers auth call. Widget tests: mock LoginFormBLoC using mocktail or bloc_test's MockBloc; assert (1) text input triggers correct event dispatch, (2) state with isLoading=true disables fields and button, (3) state with emailError non-null shows error under email field, (4) state with isValid=false disables submit button, (5) state with isValid=true enables submit button. Use whenListen to emit state sequences in widget tests.

Component
Login Form
ui medium
Epic Risks (3)
high impact medium prob scope

Automated accessibility checks (e.g., flutter_accessibility_service) may pass while manual VoiceOver/TalkBack testing reveals focus-order issues, missing semantic roles, or live region announcements that fire too early or not at all. Discovering these late risks delaying the MVP release.

Mitigation & Contingency

Mitigation: Conduct manual VoiceOver and TalkBack testing on physical devices at the end of every sprint, not only at release. Define accessibility acceptance criteria per component and include them in the DoD. Use Semantics widgets explicitly rather than relying on implicit semantics from Flutter's default widgets for all interactive elements.

Contingency: Maintain a prioritized accessibility bug backlog separate from the main backlog. If critical VoiceOver issues are found close to release, create an explicit accessibility hotfix sprint before TestFlight distribution to Blindeforbundet testers.

medium impact medium prob technical

Keyboard height varies significantly between iOS and Android, between device sizes (iPhone SE vs iPad), and with third-party keyboards. The KeyboardAwareLayout may not correctly adjust scroll offset in all combinations, causing input fields to remain hidden behind the keyboard on certain device/keyboard configurations.

Mitigation & Contingency

Mitigation: Test on a matrix of devices including iPhone SE (small viewport), a mid-size Android phone, and a tablet. Implement the layout using MediaQuery.viewInsets.bottom rather than a fixed padding value to correctly respond to any keyboard height. Include edge cases for floating keyboards on iPads.

Contingency: If device-specific issues are found after release, implement a bottom-padding fallback using BottomPadding inset and allow users to manually scroll. Log affected device/OS combinations for targeted fixes.

high impact low prob dependency

If the design token system's colour palette is updated without re-running contrast validation, form field labels, error messages, or placeholder text could fall below the WCAG 2.2 AA 4.5:1 ratio, causing a compliance regression.

Mitigation & Contingency

Mitigation: Integrate a contrast ratio validator (e.g., a CI lint step using the contrast-ratio-validator component) that checks all colour pairs used in the login form on every pull request. Document which token pairs are used for labels, errors, and backgrounds in the login form.

Contingency: If a contrast regression is detected post-merge, hot-patch the affected design token value. Do not ship a TestFlight build with known WCAG AA failures to Blindeforbundet testers.