high priority low complexity frontend pending frontend specialist Tier 1

Acceptance Criteria

NotificationTabBadge is mounted within the Notifications tab icon slot of 042-role-aware-bottom-nav using a Stack layout
Badge is visible for all user roles that have access to the Notifications tab (coordinator, peer mentor, org admin)
Badge count increments in real time when a new notification arrives via Supabase Realtime — no manual refresh required
Badge disappears immediately after the user triggers mark-all-read without any page navigation or reload
Badge does not appear for roles that do not have access to the Notifications tab
Supabase Realtime channel is established on app startup and stays active across tab switches (not re-subscribed on each navigation)
Widget test: start with count=0 (badge hidden), emit new notification event (badge shows '1'), emit MarkAllRead (badge hidden again)
Integration test: real Supabase Realtime message triggers badge update within 2 seconds on a standard device
No duplicate subscriptions created when the bottom nav widget rebuilds due to role change or theme change
Badge positioning does not break on any of the 5 tab icons — only the Notifications icon carries the badge

Technical Requirements

frameworks
Flutter
BLoC
Supabase
apis
Supabase Realtime (notifications channel)
data models
NotificationBLoC state
NotificationState (unread count)
User role model
performance requirements
Realtime subscription must not create memory leaks — channel must be disposed when the widget tree is unmounted
Badge update latency from Supabase Realtime event to rendered UI must be under 500ms on a 4G connection
security requirements
Supabase Realtime channel must be scoped to the authenticated user's notifications only (row-level security enforced at DB level)
No notification count data must be cached in a way accessible to other users on shared devices
ui components
042-role-aware-bottom-nav (existing component)
519-notification-tab-badge (task-003 output)
Stack (Flutter built-in)

Execution Context

Execution Tier
Tier 1

Tier 1 - 540 tasks

Can start after Tier 0 completes

Implementation Notes

Locate the Notifications tab item within 042-role-aware-bottom-nav and wrap its icon widget in a Stack containing the NotificationTabBadge. The existing nav bar widget likely uses NavigationBar or a custom BottomNavigationBar — identify which and wrap only the icon widget. The Supabase Realtime subscription should be initiated in the NotificationBLoC (not in the widget layer) via a dedicated event (e.g., SubscribeToNotifications) dispatched at app startup. This ensures the subscription lifecycle is managed by the BLoC and not tied to widget rebuilds.

Ensure the Supabase channel uses `.on('postgres_changes', ...)` filtering on `user_id = auth.uid()` to prevent cross-user data leakage. Dispose the channel in the BLoC's `close()` method.

Testing Requirements

Write widget tests using flutter_test with a mocked NotificationBLoC. Test the full transition cycle: (1) badge hidden at app start with 0 unread, (2) BLoC emits state with unread=1 → badge appears, (3) BLoC emits MarkAllReadSuccess → badge disappears. Also test that the badge only appears on the Notifications icon slot and not on other nav tab icons. For Supabase Realtime integration, write an integration test using a local Supabase emulator or test environment to confirm real-time delivery triggers BLoC state update.

Verify no duplicate channel subscriptions using a spy on the Supabase client.

Component
Notification Tab Badge
ui low
Epic Risks (3)
medium impact medium prob integration

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.

medium impact low prob technical

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.

high impact medium prob scope

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.