flutter_secure_storage
flutter_secure_storage copied to clipboard
Typed `flutter_secure_storage`
I've always found it a bit of a struggle that everything in flutter_secure_storage is stored and retrieved as Strings, so I spent a little while trying to make a type wrapper for flutter_secure_storage and have come up with the code attached below.
While this worked quite well for my own uses, I haven't been able to get around having to define the name of the enum (StorageKey in this case) inside the code for the wrapper. I imagine that, if it were possible to pass in the enum and retain the type derivations that StorageKey<N> is providing, it would be quite trivial to implement a more comprehensive typed implementation.
I am currently using this with success within my own app and haven't found any glaring issues thus far, so I hope that, at a minimum, this can be useful for those who want this.
Highlights
- Returns the correct type (nullable and not, based on the definition in StorageKey)
- Correctly types the inputs for the set function to prevent erroneous input
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
enum StorageKey<T> {
// Repo Manager
repoIndex<int>(defaultValue: 0),
tileSyncIndex<int>(defaultValue: 0),
tileManualSyncIndex<int>(defaultValue: 0),
onboardingStep<int>(defaultValue: 0),
erroring<String?>(defaultValue: null),
ghSponsorToken<String?>(defaultValue: null),
repoNames<List<String>>(defaultValue: []);
const StorageKey({required this.defaultValue});
final T defaultValue;
}
Type getType<T>() => T;
class Storage<T extends StorageKey> {
final FlutterSecureStorage storage;
Storage(String? name)
: storage = FlutterSecureStorage(aOptions: AndroidOptions(sharedPreferencesName: name), iOptions: IOSOptions(accountName: name));
Future<N> get<N>(StorageKey<N> key) async {
String? value = await storage.read(key: key.name.toString());
if (N == getType<String?>() || N == getType<String>()) {
if (null is N) {
return value as N;
}
return (value ?? key.defaultValue) as N;
}
if (N == getType<int?>() || N == getType<int>()) {
final finalValue = (value == null ? null : int.tryParse(value));
if (null is N) {
return finalValue as N;
}
return (finalValue ?? key.defaultValue) as N;
}
if (N == getType<List<String>?>() || N == getType<List<String>>()) {
final finalValue = value?.split(",");
if (null is N) {
return finalValue as N;
}
return (finalValue ?? key.defaultValue) as N;
}
throw Exception("Key <${key.name.toString()}> datatype unsupported!");
}
Future<void> set<N>(StorageKey<N> key, N value) async {
if (N == getType<int?>() || N == getType<int>()) {
await storage.write(key: key.name.toString(), value: (value ?? "").toString());
return;
}
if (N == getType<String?>() || N == getType<String>()) {
await storage.write(key: key.name.toString(), value: value.toString());
return;
}
if (N == getType<List<String>?>() || N == getType<List<String>>()) {
await storage.write(key: key.name.toString(), value: value == null ? "" : (value as List<String>).join(","));
return;
}
throw Exception("Key <${key.name.toString()}> datatype unsupported!");
}
}