high priority medium complexity frontend pending frontend specialist Tier 4

Acceptance Criteria

Navigating from Tab A to Tab B and back to Tab A preserves Tab A's scroll position within its ListView/CustomScrollView
A multi-step form (e.g. activity wizard) in progress on a tab is not reset when the user switches to another tab and returns
List filter/search state within a tab is preserved across tab switches
When the active role changes and the same tab is still permitted, scroll position and form state within that tab are preserved
When the active role changes and the current tab is no longer permitted, navigation resets to the home tab (index 0) cleanly without errors
StatefulShellRoute uses indexedStack strategy so all branch widgets remain mounted (not rebuilt) across tab switches
ScrollController instances are properly disposed when the tab branch is permanently unmounted (e.g. on logout)
No memory leaks introduced by keeping all branches mounted — validate with Flutter DevTools memory profiler
Behavior validated for all three roles: coordinator, peer_mentor, org_admin

Technical Requirements

frameworks
Flutter
GoRouter (StatefulShellRoute.indexedStack)
data models
ActiveRoleState
TabScrollState (local widget state)
performance requirements
Tab switch must not trigger a full widget rebuild of the returning tab
Mounted-but-hidden branches must not perform unnecessary network fetches
security requirements
Preserved form state must not persist sensitive data (e.g. personal identifiers) beyond the active session
ui components
ScrollController with AutomaticKeepAliveClientMixin per scrollable list
PageStorageKey on ListView/GridView widgets for automatic scroll restoration
StatefulShellRoute.indexedStack branch configuration

Execution Context

Execution Tier
Tier 4

Tier 4 - 323 tasks

Can start after Tier 3 completes

Implementation Notes

Use StatefulShellRoute.indexedStack (not the default StatefulShellRoute) to keep all branch widgets alive in the widget tree. Add AutomaticKeepAliveClientMixin to any StatefulWidget that needs to preserve scroll or form state — call super.build(context) and wantKeepAlive: true. Use PageStorageKey('-list') on ListView widgets for automatic scroll position restoration via PageStorage. For form state preservation, store form field values in the tab's BLoC/Riverpod provider rather than local widget state — this survives widget tree rebuilds.

When handling role switches that remove a tab, call GoRouter.go('/home') before updating the tab permission matrix to avoid GoRouter route-not-found errors.

Testing Requirements

Widget tests: (1) navigate to tab with scrollable list, scroll down 200px, switch tab, return and assert scroll offset is preserved using ScrollController.offset, (2) partially fill a form on a tab, switch tabs, return and assert form fields retain values, (3) switch role while on a preserved tab and assert state retention where role still permits the tab. Integration test: simulate full coordinator workflow (scroll contacts list, switch to Work tab, return) and assert no resets. Manual validation required on physical device for all three role scenarios. Use Flutter DevTools to confirm no memory leaks from mounted branches.

Component
Role-Aware Bottom Navigation
ui medium
Epic Risks (3)
high impact medium prob technical

Combining GoRouter's declarative redirect logic in the route guard with StatefulShellRoute's stateful branch management is known to produce subtle bugs where the shell rebuilds unnecessarily on role switches, losing tab state or causing double-navigation events.

Mitigation & Contingency

Mitigation: Implement the route guard as a GoRouter redirect callback that only evaluates role from an already-resolved Riverpod provider (not async). Use a dedicated ShellRoute navigator key per tab branch to anchor state independently of role-driven rebuilds. Write integration tests for the full navigation graph.

Contingency: If StatefulShellRoute state loss is confirmed during QA, fall back to a manual tab state preservation approach using a TabStateManager service that caches the last route per tab and restores it after role switches, decoupling tab state from the shell lifecycle.

medium impact high prob scope

The role-based home screen must render three significantly different layouts (coordinator dashboard, peer mentor activity summary, org admin overview). If these variants are implemented as a single widget with conditionals, the file will become unmaintainable and difficult to test in isolation, especially as each variant grows with downstream feature additions.

Mitigation & Contingency

Mitigation: Design the role-based home screen as a router/dispatcher widget that delegates to three separate variant widgets (CoordinatorHomeView, PeerMentorHomeView, OrgAdminHomeView). Each variant is independently testable and can be developed by separate team members in parallel.

Contingency: If variant coupling has already occurred before this risk is addressed, refactor to the dispatcher pattern in a dedicated cleanup task before feature handoff. The dispatcher pattern is a straightforward extraction that carries low refactoring risk.

medium impact medium prob integration

The no-access screen must link global admin users to the correct admin portal URL, which may differ per organization (NHF, HLF, Blindeforbundet each have their own admin portals). Hardcoding a single URL will result in wrong or broken links for some global admin users.

Mitigation & Contingency

Mitigation: Source the admin portal URL from the organization's configuration record in Supabase rather than hardcoding it. The no-access screen reads the active org context and resolves the portal URL dynamically. Provide a safe fallback to a generic Norse Digital Products support page if the URL is not configured.

Contingency: If dynamic URL resolution is not ready when the no-access screen ships, display a static instruction to contact the organization's administrator along with a support email address as an interim measure, and track the URL configuration task as a follow-up.