critical priority medium complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

OrgSelectionScreen renders a ListView or GridView of OrgCardWidget items when OrganizationRepository returns a non-empty list
Each OrgCardWidget receives orgId, orgName, logoUrl, and primaryColor sourced from OrgBrandingCache for that org
Logo images are loaded asynchronously with a placeholder shown during load and on error
An EmptyStateWidget (custom or reusable) renders when the org list is empty, with a user-friendly message
The org list is fully scrollable — all orgs reachable by vertical scroll without layout overflow
Each OrgCardWidget is keyboard-navigable: Focus traversal moves between cards in logical order (top-to-bottom)
Tapping an OrgCardWidget dispatches an OrgSelected event to the BLoC/provider and stores the org ID
No duplicate org cards appear in the list — org ID used as ListView key
List correctly handles 1, 5, 10, and 20 organizations without visual breakage
OrganizationRepository call is triggered once on screen mount, not on every rebuild
flutter analyze reports zero errors on the modified files

Technical Requirements

frameworks
Flutter
BLoC or Riverpod
GoRouter
apis
OrganizationRepository.getOrganizations()
OrgBrandingCache.getBrandingTokens(orgId)
data models
Organization
OrgBrandingTokens
performance requirements
ListView uses itemBuilder (lazy) not a pre-built children list — ensures performance for large org lists
OrgBrandingCache returns synchronously for pre-warmed orgs — no per-card async gap visible to user
Image loading uses cached_network_image or equivalent to avoid repeated network fetches on scroll
security requirements
OrganizationRepository must only return orgs the current (anonymous) user is permitted to see — enforced by Supabase RLS
No org-specific sensitive data (internal IDs, admin emails) exposed in the card rendering layer
ui components
ListView.builder (lazy list)
OrgCardWidget (per item)
EmptyStateWidget
CachedNetworkImage or FadeInImage (logo loading)
Focus (keyboard navigation wrapper)
CircularProgressIndicator (logo placeholder)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Inject OrganizationRepository and OrgBrandingCache via Riverpod providers or BLoC dependency injection — not directly instantiated in the widget. Use ListView.builder with itemCount: orgs.length and itemKey: (index) => ValueKey(orgs[index].id) to prevent duplicate rendering. For branding: if OrgBrandingCache is pre-warmed, getBrandingTokens() is synchronous — use it directly in itemBuilder. If not pre-warmed, use a FutureBuilder per card (acceptable for this screen, since org count is small — typically 3-15 partner orgs).

EmptyStateWidget: simple Column with an icon, heading 'No organizations found', and a subtext 'Please contact your coordinator'. For keyboard navigation: Flutter's default Focus traversal handles ListView items correctly if each OrgCardWidget contains a focusable widget (InkWell/GestureDetector with onTap). Explicitly set focusable: true on card if needed.

Testing Requirements

Widget tests: (1) given a list of 3 orgs, verify 3 OrgCardWidget instances render; (2) given empty list, verify EmptyStateWidget renders; (3) verify tapping first card dispatches correct OrgSelected event; (4) verify branding tokens (primaryColor, logoUrl) are passed to card. Use a fake OrganizationRepository and fake OrgBrandingCache injected via provider override. Mock image loading to avoid network in tests. Minimum 5 widget tests.

Epic Risks (2)
low impact high prob technical

OrgSelectionScreen and OrgContextSwitcher render partner-specific logos, colors, and text from dynamic data. Golden tests (pixel-comparison screenshots) will fail whenever branding assets are updated in the backend, causing CI failures that block unrelated PRs and eroding developer trust in the test suite.

Mitigation & Contingency

Mitigation: Use fixture-based golden tests with static mock Organization models containing embedded test assets rather than network-fetched assets. Separate branding asset acceptance tests into a dedicated CI job that only runs on branding-related PRs and is maintained by the design team.

Contingency: If golden test maintenance overhead becomes excessive, replace pixel-comparison goldens with semantic widget tests that assert widget tree structure and key property values, reserving golden tests for only the most stable, design-critical elements.

high impact medium prob scope

Several partner organizations (especially Blindeforbundet) have users who rely entirely on VoiceOver or TalkBack. Complex branded card layouts with overlaid logos, names, and selection states are notoriously difficult to make fully accessible; missing semantics or incorrect focus order could make the selection screen completely unusable for screen reader users before launch.

Mitigation & Contingency

Mitigation: Involve an accessibility specialist in the design review before any widget implementation begins. Use Flutter's Semantics widget with explicit label, hint, and button role on OrgCardWidget. Conduct manual screen reader testing on both iOS (VoiceOver) and Android (TalkBack) for every sprint that touches these screens, not just before release.

Contingency: If full WCAG compliance cannot be achieved within the sprint, implement a simplified text-list fallback mode that activates when the system detects an active screen reader, presenting orgs as plain accessible list tiles instead of the branded card layout.