Skip to content

Instantly share code, notes, and snippets.

@TekExplorer
Created August 29, 2025 00:11
Show Gist options
  • Select an option

  • Save TekExplorer/5bf69ee26d6ccf9b81762245ad90c3eb to your computer and use it in GitHub Desktop.

Select an option

Save TekExplorer/5bf69ee26d6ccf9b81762245ad90c3eb to your computer and use it in GitHub Desktop.
import 'dart:async';
import 'package:meta/meta.dart';
import 'package:shared_preferences/shared_preferences.dart';
/// A wrapper around [SharedPreferences] that allows for different implementations
/// of shared preferences, such as legacy, async, withCache, and a custom in-memory variant.
final class $SharedPreferences {
$SharedPreferences.legacy(SharedPreferences this._prefs);
$SharedPreferences.async(SharedPreferencesAsync this._prefs);
$SharedPreferences.withCache(SharedPreferencesWithCache this._prefs);
$SharedPreferences.inMemory({Map<String, Object?>? cache})
: _prefs = <String, Object?>{...?cache};
final Object _prefs;
Future<void> migrateTo($SharedPreferences other) async {
if (other._prefs == _prefs) return;
await other.setAll(await getAll());
}
FutureOr<void> migrateFrom($SharedPreferences other) async {
if (other._prefs == _prefs) return;
await setAll(await other.getAll());
}
@internal
T map<T>(
T Function(SharedPreferences prefs) prefs,
T Function(SharedPreferencesAsync prefs) async,
T Function(SharedPreferencesWithCache prefs) withCache,
T Function(Map<String, Object?> cache) inMemory,
) =>
switch (_prefs) {
SharedPreferences $prefs => prefs($prefs),
SharedPreferencesAsync prefs => async(prefs),
SharedPreferencesWithCache prefs => withCache(prefs),
Map<String, Object?> cache => inMemory(cache),
_ => throw 'unreachable',
};
FutureOr<void> clear() => map(
(prefs) => prefs.clear(),
(prefs) => prefs.clear(),
(prefs) => prefs.clear(),
(cache) => cache.clear(),
);
FutureOr<void> remove(String key) => map(
(prefs) => prefs.remove(key),
(prefs) => prefs.remove(key),
(prefs) => prefs.remove(key),
(cache) => cache.remove(key),
);
FutureOr<Set<String>> getKeys() => map(
(prefs) => prefs.getKeys(),
(prefs) => prefs.getKeys(),
(prefs) => prefs.keys,
(cache) => cache.keys.toSet(),
);
FutureOr<Map<String, Object?>> getAll() => map(
(prefs) => {for (final key in prefs.getKeys()) key: prefs.get(key)},
(prefs) => prefs.getAll(),
(prefs) => {for (final key in prefs.keys) key: prefs.get(key)},
(cache) => cache,
);
Future<void> setAll(Map<String, Object?> values) => [
for (final entry in values.entries)
if (entry.value case final value?) set(entry.key, value),
].wait;
FutureOr<Object?> get(String key) => map(
(prefs) => prefs.get(key),
(prefs) => prefs.getAll().then((all) => all[key]),
(prefs) => prefs.get(key),
(cache) => cache[key],
);
Future<void> set(String key, Object value) => switch (value) {
String value => setString(key, value),
int value => setInt(key, value),
bool value => setBool(key, value),
double value => setDouble(key, value),
List<String> value => setStringList(key, value),
_ => throw ArgumentError('Unsupported type: ${value.runtimeType}'),
};
FutureOr<String?> getString(String key) => map(
(prefs) => prefs.getString(key),
(prefs) => prefs.getString(key),
(prefs) => prefs.getString(key),
(cache) => cache[key]?.maybeAs<String>(),
);
Future<void> setString(String key, String value) => map(
(prefs) => prefs.setString(key, value),
(prefs) => prefs.setString(key, value),
(prefs) => prefs.setString(key, value),
(cache) async => cache[key] = value,
);
FutureOr<int?> getInt(String key) => map(
(prefs) => prefs.getInt(key),
(prefs) => prefs.getInt(key),
(prefs) => prefs.getInt(key),
(cache) => cache[key]?.maybeAs<int>(),
);
Future<void> setInt(String key, int value) => map(
(prefs) => prefs.setInt(key, value),
(prefs) => prefs.setInt(key, value),
(prefs) => prefs.setInt(key, value),
(cache) async => cache[key] = value,
);
FutureOr<bool?> getBool(String key) => map(
(prefs) => prefs.getBool(key),
(prefs) => prefs.getBool(key),
(prefs) => prefs.getBool(key),
(cache) => cache[key]?.maybeAs<bool>(),
);
Future<void> setBool(String key, bool value) => map(
(prefs) => prefs.setBool(key, value),
(prefs) => prefs.setBool(key, value),
(prefs) => prefs.setBool(key, value),
(cache) async => cache[key] = value,
);
FutureOr<double?> getDouble(String key) => map(
(prefs) => prefs.getDouble(key),
(prefs) => prefs.getDouble(key),
(prefs) => prefs.getDouble(key),
(cache) => cache[key]?.maybeAs<double>(),
);
Future<void> setDouble(String key, double value) => map(
(prefs) => prefs.setDouble(key, value),
(prefs) => prefs.setDouble(key, value),
(prefs) => prefs.setDouble(key, value),
(cache) async => cache[key] = value,
);
FutureOr<List<String>?> getStringList(String key) => map(
(prefs) => prefs.getStringList(key),
(prefs) => prefs.getStringList(key),
(prefs) => prefs.getStringList(key),
(cache) => cache[key]?.maybeAs<List<String>>(),
);
Future<void> setStringList(String key, List<String> value) => map(
(prefs) => prefs.setStringList(key, value),
(prefs) => prefs.setStringList(key, value),
(prefs) => prefs.setStringList(key, value),
(cache) async => cache[key] = value,
);
}
extension on Object? {
R? maybeAs<R extends Object>() => switch (this) {
R value => value,
_ => null,
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment