high priority medium complexity frontend pending frontend specialist Tier 2

Acceptance Criteria

EditContactScreen is a ConsumerStatefulWidget (Riverpod) or BlocProvider widget that accepts a contactId parameter
On mount, the screen reads contact data from contact-detail-service; while loading, a skeleton placeholder is shown for each field
Once data is loaded, all AppTextField instances are pre-populated with current contact values (name, email, phone, birth date)
The custom fields table widget is pre-populated with existing custom field values
The save button is disabled (visually and functionally) until contact data has fully loaded
The save button is also disabled when any field has a validation error (wired in task-015, but the disabled-state hook must be present)
Form state (dirty tracking) is managed by a dedicated StateNotifier or Bloc — not by raw TextEditingControllers stored in widget state
Navigating away with unsaved changes triggers a discard-changes confirmation dialog
Screen is accessible: all fields have semantic labels, tab order is logical, focus moves to first field after data loads
Widget test: pre-population verified by asserting AppTextField values match the mock contact data

Technical Requirements

frameworks
Flutter
Riverpod
BLoC
data models
Contact
ContactEditFormState
CustomField
performance requirements
Skeleton shown immediately on mount — no blank screen flash
Pre-population must not cause a visible re-render flicker after skeleton disappears
security requirements
Do not pre-populate encrypted fields with decrypted values — those use EncryptedFieldDisplay (component 175)
Form state must not persist beyond the screen's lifecycle — dispose StateNotifier on widget unmount
ui components
AppTextField
AppButton (save/cancel)
SkeletonLoader
CustomFieldsTableWidget
DiscardChangesDialog

Execution Context

Execution Tier
Tier 2

Tier 2 - 518 tasks

Can start after Tier 1 completes

Implementation Notes

Create a ContactEditFormNotifier (Riverpod StateNotifier) or ContactEditFormBloc with a ContactEditFormState that holds: original contact snapshot, current field values map, isLoading flag, and isDirty flag. Pre-populate by watching the contact-detail-service provider and calling notifier.initialize(contact) in a ref.listen or initState equivalent. Use TextEditingController only for AppTextField binding — keep source of truth in the notifier, not the controller. For the discard dialog, intercept pop with PopScope (Flutter 3.x) or WillPopScope.

Ensure skeleton uses the same layout dimensions as the real form to avoid layout shift. Follow the design token system for spacing and typography — no hardcoded sizes.

Testing Requirements

Widget tests using WidgetTester with ProviderScope overrides to inject a mock contact-detail-service returning a fixed Contact. Test: (1) skeleton visible while contact-detail-service is in loading state; (2) AppTextField values match contact data after load; (3) save button disabled during loading; (4) save button enabled after load (before wiring validation from task-015, assert enabled state); (5) discard dialog appears when back is pressed with dirty form state. Use `tester.pump()` to advance through async loading. No real Supabase calls.

Component
Edit Contact Screen
ui medium
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.