Register LocalStorageAdapter with Riverpod
epic-organization-selection-infrastructure-task-003 — Create a Riverpod provider for LocalStorageAdapter that initializes SharedPreferences and exposes the adapter as a singleton. Ensure the provider is available in the app's ProviderScope. Follow the existing Riverpod provider conventions used in the codebase.
Acceptance Criteria
Technical Requirements
Execution Context
Tier 2 - 518 tasks
Can start after Tier 1 completes
Implementation Notes
If the codebase uses code generation (riverpod_generator with @riverpod annotations), follow that pattern exactly — do not introduce a manual Provider if the codebase uses generated providers. If using FutureProvider (because SharedPreferences.getInstance() is async), consumers of this provider will need to handle the AsyncValue — consider whether downstream services should await the FutureProvider or whether the app's startup sequence (in main.dart or a bootstrap function) should pre-initialize SharedPreferences and provide it synchronously via ProviderScope overrides. The latter approach (pre-init in main()) is recommended for avoiding AsyncValue boilerplate throughout the app. Check how other infrastructure providers (e.g., Supabase client provider) handle async initialization and match that pattern.
Add a // ignore: avoid_manual_providers comment only if truly necessary — prefer code-gen conventions.
Testing Requirements
Write one widget test that: (1) creates a ProviderContainer with a mock LocalStorageAdapter override; (2) reads localStorageAdapterProvider and asserts it returns the mock instance. This verifies that the provider is overridable for downstream tests. Additionally, verify in a separate test that the real provider (using SharedPreferences.setMockInitialValues) returns a non-null LocalStorageAdapter instance.
No integration tests against a real device are needed for this task.
SharedPreferences behaves differently on iOS (NSUserDefaults) vs Android (SharedPreferences) for edge cases such as first-launch cold reads, storage quota exceeded, or process kill mid-write. If the adapter does not account for these differences, the persistence layer can silently return null on one platform while returning a stale value on the other, causing incorrect routing decisions downstream.
Mitigation & Contingency
Mitigation: Write platform-specific integration tests using flutter_test device runners for both iOS and Android. Document known platform delta in the adapter's inline comments and encode defensive fallback for null returns at the repository boundary.
Contingency: If platform delta causes persistent issues, replace SharedPreferences with flutter_secure_storage for this key — the LocalStorageAdapter abstraction makes this a single-file swap with no impact on the repository or service layer.
The shared_preferences Flutter plugin may have a version conflict with other plugins already in the project pubspec.yaml. A conflict discovered late in the epic blocks all downstream epics.
Mitigation & Contingency
Mitigation: Resolve and pin the shared_preferences version in pubspec.yaml as the very first task of this epic before writing any implementation code. Run flutter pub get and resolve any version conflicts immediately.
Contingency: If version pinning is impossible due to transitive conflicts, implement LocalStorageAdapter using path_provider + dart:io for JSON file storage as an alternative — the interface contract remains unchanged.