Implement reactive flag change propagation
epic-organization-feature-flags-runtime-task-006 — Ensure that when an admin toggles a feature flag (e.g., via the admin screen), the FeatureFlagProvider detects the state change (via repository listener or cache invalidation signal) and notifies all Riverpod watchers, causing dependent widgets to rebuild reactively without requiring a full app restart.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 5 - 253 tasks
Can start after Tier 4 completes
Implementation Notes
Implement using Riverpod's `StateNotifier` or `Notifier` (Riverpod 2.x). The provider watches a `StreamProvider` backed by the Supabase Realtime channel for the `feature_flags` table scoped to the current organization. On stream event, parse the changed flag, recompute only the affected entry in the resolved map (immutable copy with updated entry using `Map.from(...)..update(...)`), and set state to the new map. For selective rebuilds, expose individual flag providers via `provider.select((map) => map[flagKey])` so each FeatureGate widget only re-renders when its specific flag changes.
For org switching, listen to an `activeOrganizationProvider` and use `ref.watch` to auto-dispose and re-create the flag stream when the org changes. Add a `StaleCacheNotifier` bool to signal UI when a reactive update failed.
Testing Requirements
Unit and integration tests using flutter_test. Test cases: (1) Mock repository emits a change event → verify provider rebuilds and isEnabled returns updated value; (2) Mock org switch event → verify map is cleared and re-initialized with new org's flags; (3) Simulate rapid 5 consecutive toggles within 100ms → verify only 1 or 2 rebuilds occur (debounce working); (4) Mock network failure during reactive update → verify stale values retained and no exception thrown; (5) Widget test: wrap a FeatureGate widget in a ProviderScope, simulate flag toggle via mock repository, assert widget content changes without app restart. Use flutter_test WidgetTester for widget-level tests.
The feature-flag-initializer must complete before any screen that checks feature flags renders. If the navigation shell is pushed concurrently with initialization (e.g., via a parallel Riverpod provider chain), some screens may query flags before they are loaded and incorrectly receive all-disabled defaults.
Mitigation & Contingency
Mitigation: Gate the main navigation shell render behind the feature-flag-initializer's Future by using a splash/loading screen that awaits the initialization provider. Use Riverpod's ref.watch on an initialization state enum (loading/ready/error) to block rendering.
Contingency: If race conditions are observed in testing, introduce an explicit initialization barrier using a ChangeNotifier or a dedicated `featureFlagsReadyProvider` that the router guard checks before allowing navigation.
If feature-flag-provider is watched by many widgets simultaneously and a flag map refresh triggers all of them to rebuild at once (e.g., after an organization switch), the app could experience a significant UI jank or dropped frames.
Mitigation & Contingency
Mitigation: Use select() on the provider to have each widget watch only the specific flag key it needs rather than the entire map. Ensure the provider uses equality checks so rebuilds only propagate when the specific flag value changes.
Contingency: If rebuild storms are measured via Flutter DevTools, refactor to a family provider keyed by flag key, so each widget subscribes only to its own flag's changes.