Build Notification Filter Bar chip row widget
epic-in-app-notification-centre-ui-task-001 — Implement the NotificationFilterBar as a horizontally scrollable chip row widget that renders filter options for notification type (assignment_reminder, certification_expiry, pause_status, scenario_prompt) and read-state (all, unread, read). Each chip emits a FilterChanged event to the NotificationBLoC when tapped. Active filters must be visually distinguished using design token accent colors. The widget must be fully accessible with WCAG 2.2 AA semantics, including Semantics wrappers, meaningful labels, and keyboard/switch-access support.
Acceptance Criteria
Technical Requirements
Implementation Notes
Build NotificationFilterBar as a StatelessWidget accepting a currentFilter parameter and an onFilterChanged callback (or wired directly to BLoC in task-002 — keep it pure in this task). Use FilterChip or a custom chip widget styled with design tokens. Access accent color via AppColors.accentPrimary or equivalent design token constant — never use Color(0xFF...) literals in widget code. For accessibility, wrap each chip in Semantics(label: '...', button: true, selected: isActive).
For WCAG contrast, verify using the Flutter accessibility checker or manually compute contrast ratio. Separate the chip data (label, type, value) into a const list of chip descriptors to avoid repetitive widget code. Use clipBehavior: Clip.none on the scroll view to prevent chip shadow clipping. Test on both light and dark theme if the app supports it.
Testing Requirements
Widget tests using flutter_test. Cover: (1) all 7 chips render with correct labels, (2) active chip has the correct accent color from design tokens, (3) inactive chips have the correct muted color, (4) tapping a type chip triggers the correct callback/event, (5) tapping a read-state chip triggers the correct callback/event, (6) Semantics tree contains meaningful labels for all chips (use find.bySemanticsLabel), (7) widget renders without overflow on 320px wide screen. Use mockito or mocktail to verify event dispatch. No integration tests required at this stage.
If a referenced entity (contact, certification, activity) has been deleted or its RLS policy now excludes the current user, the deep link handler may navigate to a screen that renders in an error state or throws an unhandled exception.
Mitigation & Contingency
Mitigation: The deep link handler must perform a lightweight existence check (HEAD request or minimal SELECT) before pushing the route. Define a contract with each destination screen for how to handle a not-found entity ID passed as a route parameter.
Contingency: If the existence check itself fails (network error), navigate to the destination screen anyway and let it handle the error gracefully with its own error state; do not block navigation for network timeouts.
If the tab badge widget triggers a full rebuild of the bottom navigation bar on every unread count change, it will cause visible jank on devices with many active Realtime events (e.g., org admins receiving org-wide alerts).
Mitigation & Contingency
Mitigation: Scope the badge widget to a dedicated BlocSelector that rebuilds only when the unread count value changes, not on any BLoC state emission. Use RepaintBoundary to isolate the badge from the rest of the nav bar.
Contingency: If performance issues persist, debounce badge updates to a maximum of one rebuild per 500ms and display the last known count during the debounce window.
Complex swipe-to-mark-read gestures and dynamic list updates may conflict with VoiceOver/TalkBack navigation patterns, particularly for Blindeforbundet users who rely exclusively on screen readers.
Mitigation & Contingency
Mitigation: Provide a dedicated accessibility action (Semantics.onTap / CustomSemanticsAction) for mark-as-read on each list item so screen reader users do not need the swipe gesture. Test with VoiceOver on iOS and TalkBack on Android before each release.
Contingency: If the swipe gesture proves incompatible with assistive technologies, disable it when a screen reader is detected (via ScreenReaderDetectionService) and rely solely on the tap-to-read and accessible action pathways.