bible-tracker/lib/services/tracker_storage.dart
2026-04-20 14:39:49 -04:00

51 lines
1.6 KiB
Dart

import 'dart:convert';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/tracker.dart';
/// Persists the list of trackers to [SharedPreferences] as JSON.
///
/// A repository is justified here because storage is asynchronous, has
/// specialized queries (save/load all), and is consumed both by the service
/// layer and by tests with fake implementations.
class TrackerStorage {
TrackerStorage(this._prefs);
static const String _storageKey = 'bible_tracker.trackers.v1';
final SharedPreferences _prefs;
Future<List<Tracker>> loadAll() async {
final String? raw = _prefs.getString(_storageKey);
if (raw == null || raw.isEmpty) {
return <Tracker>[];
}
final List<dynamic> decoded = jsonDecode(raw) as List<dynamic>;
return decoded
.cast<Map<String, dynamic>>()
.map(Tracker.fromJson)
.toList(growable: false);
}
Future<void> saveAll(List<Tracker> trackers) async {
final List<Map<String, dynamic>> json = trackers
.map((Tracker tracker) => tracker.toJson())
.toList(growable: false);
await _prefs.setString(_storageKey, jsonEncode(json));
}
}
/// Must be overridden at startup with an initialized [SharedPreferences].
final Provider<SharedPreferences> sharedPreferencesProvider =
Provider<SharedPreferences>(
(Ref ref) => throw UnimplementedError(
'sharedPreferencesProvider must be overridden before use',
),
);
final Provider<TrackerStorage> trackerStorageProvider =
Provider<TrackerStorage>(
(Ref ref) => TrackerStorage(ref.read(sharedPreferencesProvider)),
);