I've been learning Flutter and learned this to improve usage with Pub.dev package Shared Preferences,
To sum up, it is to create a service that I can consume and work in a better way all around my app with SharedPreferences, it works perfectly on my Code for token because I have my async function
So, the getMethod works perfectly using final token = await keyValueStorageService.getValue<String>('token') (also async function above),
but I can't manage to use this code on my UI code, it gives me this instead (Because of the lack of an await function)
but as I read you can use it and initiate (shown below) on your main.dart, I've been trying to use a method getSharedPrefs() on the main.dart to implement and use it all around my app for example calling my getValue('user') wherever I need to but haven't made it work
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Enviroment.initEnvirontment();
//! Key Value instance for main async
await KeyValueStorageServiceImpl().getSharedPrefs();
await initializeDateFormatting('es_ES', null);
SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
.then((value) => runApp(ProviderScope(child: MyApp())));
}
My actual code is this
key_value_storage.dart
import 'key_value_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
class KeyValueStorageServiceImpl extends KeyValueStorageService {
Future<SharedPreferences> getSharedPrefs() async {
return await SharedPreferences.getInstance();
}
@override
Future<T?> getValue<T>(String key) async {
final prefs = await getSharedPrefs();
switch (T) {
case String:
return prefs.getString(key) as T?;
case int:
return prefs.getInt(key) as T?;
case double:
return prefs.getDouble(key) as T?;
case bool:
return prefs.getBool(key) as T?;
case List:
return prefs.getStringList(key) as T?;
default:
throw UnimplementedError(
'No implementation Get for ${T.runtimeType}');
}
}
@override
Future<bool> removeKey(String key) async {
final prefs = await getSharedPrefs();
return await prefs.remove(key);
}
@override
Future<void> setKeyValue<T>(String key, T value) async {
final prefs = await getSharedPrefs();
switch (T) {
case String:
await prefs.setString(key, value as String);
break;
case int:
await prefs.setInt(key, value as int);
break;
case double:
await prefs.setDouble(key, value as double);
break;
case bool:
await prefs.setBool(key, value as bool);
break;
case List:
await prefs.setStringList(key, value as List<String>);
break;
default:
throw UnimplementedError(
'No implementation Set for ${T.runtimeType}');
}
}
}
key_value_class.dart
abstract class KeyValueStorageService {
Future<void> setKeyValue<T>(String key, T value);
Future<T?> getValue<T>(String key);
Future<bool> removeKey(String key);
}


What & why I made the changes:
Store an instance of
SharedPreferencesin theKeyValueStorageServiceImplclass directly and initialize with the constructor. This makes sure that the once themainfunction initializesSharedPreferencesonce, there is no subsequent need of callinggetInstanceagain inside thegetValuefunction.The
getfunctions ofSharedPreferencesreturn the T? value directly and not Future<T?> value, hence there is no need for thegetValuefunction to returnFuture<T?>, so I changed it's definition.Changed the function signature in
key_value_class.dartChanged the main function to pass a
SharedPreferencesobject to the constructor ofKeyValueStorageServiceImpl.Gave an alternative approach by making the
KeyValueStorageServiceImplinto a Singleton.You can read my comments in the code if you like.
key_value_storage.dartkey_value_class.dartmain()functionAlternative Approach using Singleton
key_value_storage.dartmain()functionNow wherever you need to get a value, you can just do the following
var value = KeyValueStorageServiceImpl.instance.getValue<Type>('key');Hope this solves your issue! Happy Coding! :)
How the Singleton approach works:
Basically, a Singleton class means that only one instance of that Class can be created, and hence it gets the name, Singleton. A Singleton class has only one static instance of itself, and as such, the static instance is available throughout the lifecycle of the app, from the app initializing to the app being closed. Thus, A Singleton enables us to declare global data and functions, which can be accessed from any class, as we don't need the
BuildContextlike in the case ofInheritedWidgets.