Implement ContactCardWidget layout and content
epic-contact-list-management-ui-components-task-002 — Build the ContactCardWidget Flutter widget displaying contact name as the primary label, a color-coded role badge, an organization label derived from the org-labels provider, and a truncated notes preview capped at two lines with ellipsis overflow. Wire the onTap callback to pass the contact ID to the parent navigator. Use design token values for spacing, typography, and border radius.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 1 - 540 tasks
Can start after Tier 0 completes
Implementation Notes
Place widget in lib/features/contacts/presentation/widgets/contact_card_widget.dart. Extract the role badge into a private _RoleBadgeWidget or reuse a shared RoleBadge widget if it already exists in the codebase. Retrieve design tokens via Theme.of(context).textTheme and a project-specific AppTokens class (or equivalent) — never hardcode colors or sizes. For the org label, accept it as a pre-resolved String parameter rather than reading from a provider inside the widget; this keeps the widget purely presentational and easier to test in isolation.
The parent list widget (built in a later task) is responsible for resolving org labels via Riverpod. Use InkWell with borderRadius matching the card's radius for the tap ripple effect to align with Material Design conventions. Ensure semantic labels are set for accessibility: Semantics(label: '${contact.name}, ${roleLabel}, ${orgLabel}') wrapping the card content for screen reader support (WCAG 2.2 AA, critical for Blindeforbundet users).
Testing Requirements
Widget tests using flutter_test: (1) renders name correctly from mockCoordinatorContact fixture, (2) renders peer_mentor role badge with correct color token, (3) notes preview is absent when notes is null (mockContactNullNotes fixture), (4) long notes are truncated to 2 lines with ellipsis (mockContactLongNotes fixture — assert maxLines behavior via finder), (5) onTap callback is invoked with correct contact ID when card is tapped (use tester.tap()), (6) renders without overflow at 200% textScaleFactor. Run flutter analyze to ensure no lint warnings.
Design token color values used in role badges, certification status indicators, and availability chips may not meet the WCAG 2.2 AA contrast ratio of 4.5:1 when rendered against card backgrounds, requiring rework after accessibility review and potentially blocking acceptance sign-off.
Mitigation & Contingency
Mitigation: Run the contrast-ratio-validator on every new token combination during widget development. Enforce the CI accessibility lint runner on all PRs touching visualization components, and validate against the contrast-safe-color-palette before finalizing card designs.
Contingency: If contrast failures are found late, adjust token values in the design token theme centrally — since all widgets consume design tokens rather than hardcoded colors, all affected widgets will be corrected by a single token update without per-widget changes.
The ContactViewSwitcher is required for Barnekreftforeningen but must not appear for other organizations. If the organization labels provider does not yet expose a reliable feature flag for this widget, it may render universally or be conditionally hidden in an inconsistent way, breaking the role-specific layout contract.
Mitigation & Contingency
Mitigation: Implement view switcher visibility as a constructor parameter on ContactListScreen injected from a provider, defaulting to hidden. Document the integration point for the org labels provider so the flag can be wired without changing the widget's API.
Contingency: If org labels integration is delayed beyond this epic, use a feature flag constant keyed to the Barnekreftforeningen organization ID as a temporary gate, with a tracked issue to replace it with the runtime labels provider before general release.