high priority medium complexity frontend pending frontend specialist Tier 3

Acceptance Criteria

MultiOrgContextSwitcher is implemented as a modal bottom sheet or slide-over panel, openable from the settings/hamburger menu
On open, MultiOrgMembershipResolver.getMemberships(userId) is called and the switcher enters a loading state
A loading skeleton (shimmer or placeholder cards matching OrgCardWidget dimensions) is shown while memberships load
Each org membership is rendered as an OrgCardWidget list item
The currently active org is visually distinguished: checkmark icon (✓) and accent color applied to its card, with the label 'Active' or equivalent
The active org's OrgCardWidget is not tappable (or tap is a no-op with no state change)
If the user belongs to only one org, the switcher still renders correctly with that single org highlighted as active
If MultiOrgMembershipResolver returns an error, an inline error message with a retry action is shown
The switcher is accessible: each list item has a Semantics label indicating org name and whether it is active
The switcher can be dismissed by tapping outside (barrier) or via a close button
Widget test verifies loading skeleton renders during fetch, org list renders on success, active org is marked correctly

Technical Requirements

frameworks
Flutter
flutter_bloc
apis
MultiOrgMembershipResolver.getMemberships(userId)
TenantContextService.getActiveOrgId()
data models
OrgMembership (orgId, orgName, orgLogoUrl, isActive)
TenantContext
performance requirements
Membership fetch must be initiated on switcher open, not on app start, to avoid unnecessary API calls
Skeleton must render within one frame of open — do not wait for fetch before showing UI
security requirements
Only orgs the authenticated user is a member of must be returned — server-side filtering enforced in MultiOrgMembershipResolver
Active org ID comparison must use server-authoritative ID, not a locally cached value that could be stale
ui components
MultiOrgContextSwitcher (modal bottom sheet wrapper)
OrgCardWidget (reused from OrgSelectionScreen, with isActive and isSelectable params)
Shimmer/skeleton placeholder cards
Checkmark icon overlay or badge on active org card
Close button in sheet header

Execution Context

Execution Tier
Tier 3

Tier 3 - 413 tasks

Can start after Tier 2 completes

Implementation Notes

Implement a MultiOrgContextSwitcherCubit with states: MultiOrgLoading, MultiOrgLoaded(memberships, activeOrgId), MultiOrgError. Open the switcher via showModalBottomSheet and pass the Cubit via BlocProvider.value so its lifecycle is not tied to the sheet. Trigger the fetch in the Cubit's constructor or via an explicit LoadMemberships event dispatched from the sheet's initState equivalent (use a StatefulWidget wrapper if using showModalBottomSheet). Extend OrgCardWidget to accept isActive (bool) and isSelectable (bool) parameters — the active card should render a checkmark trailing icon and use the design token accent color.

For the loading skeleton, build a fixed-height shimmer widget that mirrors the OrgCardWidget dimensions. Use design tokens for all colors and spacing — no hardcoded hex values.

Testing Requirements

Write widget tests: (1) switcher shows skeleton immediately on open before fetch completes, (2) org list renders correctly from mock membership data, (3) active org card shows checkmark and accent color, (4) inactive org cards are tappable (emit event), (5) active org card tap is a no-op, (6) error state shows retry button. Write Cubit unit tests: (7) emits [loading, loaded] on successful fetch, (8) emits [loading, error] on resolver failure. Use flutter_test, bloc_test, and mock MultiOrgMembershipResolver.

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.