critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

LoginForm renders two AppTextField instances: one for email (keyboardType: TextInputType.emailAddress, textInputAction: TextInputAction.next) and one for password (obscureText toggleable, textInputAction: TextInputAction.done)
PasswordVisibilityToggle is embedded as the suffixIcon of the password AppTextField and toggles obscureText state correctly
Focus traversal order is: email field → password field → PasswordVisibilityToggle → submit button, implemented via explicit FocusNode chain with TextInputAction.next on email field
Pressing the keyboard 'next' action on the email field moves focus to the password field without any user gesture
Pressing the keyboard 'done' action on the password field triggers form submission (same as tapping submit button)
Tapping anywhere outside the form fields (on background) dismisses the software keyboard via GestureDetector wrapping the form with FocusScope.of(context).unfocus()
LoginForm exposes a GlobalKey<FormState> or equivalent for external validation triggering
All interactive elements meet WCAG 2.2 AA minimum touch target size of 44×44 dp
Widget is stateless or uses minimal local state; business logic delegated to BLoC (wired in a later task)
GestureDetector uses HitTestBehavior.opaque so tap-outside capture covers the full screen area below the form

Technical Requirements

frameworks
Flutter
BLoC
performance requirements
Widget rebuild on focus change must not cause full subtree rebuild — use targeted StatefulWidget or ValueNotifier for obscureText toggle
FocusNode instances must be disposed in dispose() to prevent memory leaks
security requirements
Password field must always start with obscureText: true — visibility toggle is opt-in
Do not log or print field values at any point
Autocomplete should be set to AutofillHints.password for the password field to enable OS credential manager support
ui components
AppTextField (email instance)
AppTextField (password instance)
PasswordVisibilityToggle
AppButton (submit)
GestureDetector (keyboard dismiss wrapper)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Create LoginForm as a StatefulWidget to own the three FocusNodes (emailFocus, passwordFocus, submitFocus) and the passwordVisible ValueNotifier. Initialise FocusNodes in initState and dispose them in dispose(). Pass onFieldSubmitted callbacks to AppTextField: email's callback calls passwordFocus.requestFocus(), password's callback triggers the submit handler. Wrap the entire Column in a GestureDetector with behavior: HitTestBehavior.opaque and onTap: () => FocusScope.of(context).unfocus().

For the PasswordVisibilityToggle, use a ValueListenableBuilder so only the suffix icon rebuilds on toggle — avoid calling setState on the whole form. Use AutofillGroup around the two text fields to support OS credential managers (important for users with motor impairments who rely on autofill).

Testing Requirements

Write widget tests using flutter_test. Test that (1) both AppTextField instances render with correct keyboard types and input actions, (2) tapping the PasswordVisibilityToggle changes the password field's obscureText state, (3) focus moves from email to password field when TextInputAction.next is activated, (4) tapping outside the form calls FocusScope.unfocus (mock FocusScope or verify keyboard dismissed state), (5) submit button is present and tappable. Use WidgetTester.pumpAndSettle after focus changes. Coverage target: 90% of widget code paths.

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.