critical priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

Tapping the lock icon in EncryptedFieldDisplay shows the SensitiveFieldWarningDialog as a modal bottom sheet
The dialog displays the field label and a warning message about viewing sensitive information
Tapping the confirm button in the dialog triggers decryptField and transitions the widget to revealed state showing plaintext
Dismissing the dialog via the back button (Android), swipe-down gesture, or tapping outside the sheet does NOT trigger decryption and the field remains masked
The dialog's result (confirmed vs dismissed) is returned as a bool via Navigator.pop and is handled by the parent widget/BLoC
Decryption errors (EncryptionException) are caught and shown as a user-visible SnackBar error without exposing technical details
While decryption is in progress, the lock icon is replaced by a CircularProgressIndicator of the same 44x44 size
The revealed state renders the plaintext value using the same label row layout as masked state, with an unlock icon replacing the lock icon
The unlock icon has semantics label 'Hide [label]' and tapping it re-masks the field immediately without animation delay
Widget tree contains the plaintext string only when the widget is in revealed state

Technical Requirements

frameworks
Flutter
Dart
BLoC
Riverpod
apis
field-encryption-utils.decryptField
data models
EncryptionException (from task-005)
performance requirements
The modal sheet must appear within one frame of the tap event (use showModalBottomSheet with no artificial delay)
Decryption must run in a background isolate (via compute()) if it exceeds 8 ms to prevent jank
security requirements
Plaintext must only be stored in a local ephemeral variable in the widget's state; never written to SharedPreferences, Hive, or clipboard automatically
If the user navigates away (widget disposed), any in-progress decryption Future result must be discarded and not applied to state
ui components
SensitiveFieldWarningDialog (component 607 — must already exist or be stubbed)
CircularProgressIndicator for loading state
SnackBar for decryption error display
Unlock icon: Icons.lock_open_outlined

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Convert EncryptedFieldDisplay to a StatefulWidget (or use a ConsumerStatefulWidget if Riverpod is the DI approach) to manage the _isRevealed and _isDecrypting booleans and the _plaintextValue String?. The confirm callback from SensitiveFieldWarningDialog should `await showModalBottomSheet(...)` — the dialog calls Navigator.pop(true) on confirm and Navigator.pop(false) (or nothing, which defaults to null) on dismiss. Treat null result the same as false. In the confirm branch: set _isDecrypting = true, call compute(_decrypt, args) where _decrypt is a top-level function taking (ciphertext, key), then on result set _isRevealed = true and _plaintextValue = result.

In the catch branch, show ScaffoldMessenger.of(context).showSnackBar with a generic message ('Unable to reveal field — please try again'). Override dispose() to set a _mounted guard to prevent setState after disposal. Do not store the decryption key in widget state; re-derive it from the session token on each reveal.

Testing Requirements

Widget tests (flutter_test): (1) tap lock icon → assert SensitiveFieldWarningDialog is present in the widget tree; (2) confirm dialog → mock decryptField to return 'test value' → assert plaintext 'test value' is visible and lock icon is replaced by unlock icon; (3) dismiss dialog via Navigator.pop(false) → assert widget remains in masked state and decryptField is never called; (4) mock decryptField to throw DecryptionFailure → confirm dialog → assert SnackBar with user-friendly error appears and field stays masked; (5) tap unlock icon while in revealed state → assert field returns to masked state and plaintext is no longer in the widget tree. All tests use pumpAndSettle() after async operations.

Component
Encrypted Field Display Widget
ui high
Epic Risks (3)
medium impact medium prob scope

The encrypted-field-display confirmation dialog adds interaction steps that may frustrate coordinators who access sensitive fields frequently, leading to requests to bypass the flow or skip read-receipt logging, which would violate Blindeforbundet's compliance requirements.

Mitigation & Contingency

Mitigation: Design the confirmation dialog to be as minimal as possible (one clear sentence, single confirm action) and ensure it does not reappear for the same field within a single screen session. Validate the UX with Blindeforbundet coordinators during the TestFlight pilot before finalising.

Contingency: If coordinators raise strong objections, escalate to Blindeforbundet's data protection officer to determine whether a lighter confirmation pattern (e.g., biometric confirmation instead of dialog) satisfies their compliance obligation.

medium impact medium prob technical

The activity-history-list infinite scroll requires paginated Supabase queries. Contacts with hundreds of activities (e.g., an HLF peer mentor with 380 annual registrations) could cause slow page loads or memory pressure on older devices if pagination boundaries are set too large.

Mitigation & Contingency

Mitigation: Use a page size of 20 records with cursor-based pagination. Implement list item recycling using Flutter's ListView.builder. Benchmark memory usage with 400+ item simulation on a low-end test device before TestFlight release.

Contingency: If performance degrades on older devices, reduce page size to 10 and add a time-window filter (last 30 days, last 6 months, all) so the default view loads a manageable record count for most coordinators.

low impact high prob scope

The cognitive load rule engine (from the Cognitive Accessibility feature) mandates no more than 7 fields per screen section. If a contact model has more than 7 editable fields, the edit-contact-screen layout must be split into sections, adding complexity not accounted for in the initial scope.

Mitigation & Contingency

Mitigation: Audit the full contact field list from all four organisations before implementation. Group fields into logical sections (personal info, contact details, affiliation) so no single section exceeds 7 fields. Use the cognitive-load-rule-engine component if it is already delivered by the Cognitive Accessibility feature.

Contingency: If the rule-engine component is not yet available, implement a simple manual section layout with accordion-style expansion for less-frequently edited fields to stay within the 7-field guideline without blocking delivery.