import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; /// Base class for services that use SharedPreferences singleton pattern. /// /// This class handles the boilerplate for singleton initialization and /// SharedPreferences lifecycle management. Subclasses should: /// 1. Create a private named constructor (e.g., SettingsService._()) /// 2. Implement their own getInstance() method that calls BaseSharedPreferencesService.initializeInstance() /// 3. Optionally override onInit() for post-initialization setup abstract class BaseSharedPreferencesService { static final Map _instances = {}; late SharedPreferences _prefs; /// Protected constructor for subclasses BaseSharedPreferencesService(); /// Access to SharedPreferences instance SharedPreferences get prefs => _prefs; /// Initialize the SharedPreferences instance /// /// This method handles: /// - Singleton instance management /// - SharedPreferences initialization /// - Calling onInit() hook for subclass-specific setup static Future initializeInstance(T Function() constructor) async { if (_instances[T] == null) { final instance = constructor(); _instances[T] = instance; instance._prefs = await SharedPreferences.getInstance(); await instance.onInit(); } return _instances[T] as T; } /// Decode a JSON string to a Map with error handling. /// /// If [legacyStringOk] is true and the value is a plain string (not valid /// JSON), returns `{'key': jsonString, 'descending': false}` for legacy /// library sort compatibility. Map decodeJsonStringToMap(String jsonString, {bool legacyStringOk = false}) { try { return json.decode(jsonString) as Map; } catch (e) { if (legacyStringOk) { return {'key': jsonString, 'descending': false}; } return {}; } } /// Hook for subclass-specific initialization after SharedPreferences is ready. /// /// Override this method to perform any setup that requires access to /// SharedPreferences (e.g., registering values with other services). Future onInit() async { // Default implementation does nothing } }