high priority low complexity frontend pending frontend specialist Tier 0

Acceptance Criteria

KeyboardAwareLayout widget wraps any child and exposes a required child: Widget parameter
When the software keyboard opens, the ScrollView automatically scrolls so the currently focused TextField is fully visible above the keyboard
When the keyboard closes, the scroll position returns to its pre-keyboard state without visual jump
Scroll animation uses a curve (e.g., Curves.easeOut, 200ms) — no instant jumps
On devices with viewport height under 600dp (e.g., iPhone SE), the layout does not overflow or clip the focused field
Tapping outside a TextField dismisses the keyboard (GestureDetector with FocusScope.of(context).unfocus())
Widget is stateless-compatible: works without storing FocusNode externally; parent can optionally pass FocusNodes
Accessibility: VoiceOver/TalkBack users are not disrupted by the scroll adjustment (no unexpected focus loss)
Widget renders correctly in both portrait and landscape orientation

Technical Requirements

frameworks
Flutter
performance requirements
Keyboard animation response latency under 16ms (one frame at 60fps)
No rebuilds of children that do not depend on keyboard state — use AnimatedPadding or BottomPadding pattern, not setState on parent
ui components
SingleChildScrollView
AnimatedPadding
GestureDetector (tap-to-dismiss)
MediaQuery (viewInsets.bottom)

Execution Context

Execution Tier
Tier 0

Tier 0 - 440 tasks

Implementation Notes

The simplest robust pattern is to wrap the content in a SingleChildScrollView with padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom). Wrap this in an AnimatedContainer or use AnimatedPadding for the smooth transition. Avoid the keyboard_avoider package — it has known issues with nested scrollables. Do not use resizeToAvoidBottomInset: true on the Scaffold in combination with this widget — choose one approach.

For the login screen specifically (two fields: email + password), the scroll offset will be small; the widget becomes critical on the registration or profile screens with more fields. Since this app serves users with motor and cognitive disabilities (NHF requirement), ensure the dismiss-on-tap area is large and does not conflict with button tap targets.

Testing Requirements

Widget tests using flutter_test: (1) pumpWidget with a simulated keyboard inset via MediaQuery override and assert the scroll controller offset increases; (2) assert offset returns to 0 when viewInsets.bottom returns to 0; (3) assert no RenderFlex overflow error on a 320x568 viewport (iPhone SE equivalent). Golden tests for layout at keyboard-open and keyboard-closed states. Manual QA on physical iOS and Android devices — TestFlight build for accessibility testing with VoiceOver enabled.

Component
Keyboard-Aware Layout Utility
infrastructure low
Epic Risks (2)
high impact low prob integration

Supabase client initialization may fail silently in certain Flutter environments if environment variables are missing or the anon key is rotated, leading to runtime null-pointer errors throughout the auth layer.

Mitigation & Contingency

Mitigation: Add explicit assertion checks during app startup that verify the Supabase client is initialized before the router resolves. Document required --dart-define keys in the project README and add a CI step that validates their presence.

Contingency: Implement a fallback initialization error screen with a clear message and a retry button. Log initialization failures to crash reporting immediately.

medium impact medium prob technical

The flutter_secure_storage package behaviour differs between iOS Keychain and Android Keystore implementations. On Android, biometric-enrolled devices may require additional authentication to read stored tokens, causing unexpected session read failures.

Mitigation & Contingency

Mitigation: Test the repository on Android devices with and without biometric enrollment early in development. Use accessibility options in flutter_secure_storage to configure whether biometric authentication is required for storage access.

Contingency: If biometric-gated storage causes regressions, fall back to a non-biometric storage option for session tokens (reserving biometric-gated storage for higher-sensitivity credentials only).