Flutter, how to set and get list of values in shared preferences - flutter

I have SharedPreferencesHelper class where I stored simple data that I need.
I came across an issue. I need to store 3-4 strings in the list in shared preferences. How can I do the setter and getter for it?
SharedPreferencesHelper class:
class SharedPreferencesHelper {
static SharedPreferences? _preferences;
Future<void> init() async {
_preferences ??= await SharedPreferences.getInstance();
}
String get username => _preferences?.getString(keyUsername) ?? '';
set username(String value) {
_preferences?.setString(keyUsername, value);
}
// get aliases
List<String>? get aliases => _preferences?.getStringList(keyAliases);
// set aliases
}

You can do this to save list of string:
var pref = await SharedPreferences.getInstance();
pref.setStringList('someList',['1','2','3']);
and get it like this:
var result = pref.getStringList('someList');
if (result != null) {
print('result= $result'); //result= [1, 2, 3]
}

You can write the code like that :
class SharedPreferencesService {
SharedPreferencesServiceImpl(this.preferences);
final SharedPreferences preferences;
/// Static Variables
static const _userId = 'user_id';
#override
Future<String?> get userId async => preferences.getString(_userId);
#override
Future<void> setUserId(String value) async =>
await preferences.setString(_userId, value);
#override
Future<void> clear() async => await preferences.clear();
}
Moreover, you have to inject SharedPreferencesInstance in your getIt (recommended to use)
final getIt = GetIt.instance;
Future<void> setupExternals() async {
final prefs = await SharedPreferences.getInstance();
getIt.registerLazySingleton(() => prefs);
getIt.registerLazySingleton<SharedPreferencesService>(
getIt());
}

Related

Flutter shared prefernce return NULL

I have next piece of flutter code, to get shared preference key-value
I do understand why _blueUriInit is always NULL
I assume that you are forgot to provide the value for that key before call to get its value, you need to first assign value to it first:
Future<bool> saveData(String key, dynamic value) async {
final prefs = await SharedPreferences.getInstance();
return prefs.setString(key, value);
}
and call it like this:
void initState() {
saveData('blueUri', 'test');
setState(() {
_blueUriInit = getValue('blueUri');
});
super.initState();
}
now next time you open your app, getValue should return you test.
you can create this function for setting value
static setUserID(String key, String value) async {
final SharedPreferences preferences = await SharedPreferences.getInstance();
preferences.setString(key, value);
}
Use case :
await SharedValue.setUserID("Email", "demo#gmail.com");
And For getting value from shared preference you can use this function
static Future<String?> getUserID(String key) async {
final SharedPreferences preferences = await SharedPreferences.getInstance();
return preferences.getString(key);
}
Use case :
userName = await SharedValue.getUserID("Email");
First you need to setString with key and value (name is key)
Future setValue() async {
final prefs = await SharedPreferences.getInstance();
prefs.setString("name", "Hitarth");
}
getString with key (here i took "name" as key)
Future getValue(String key) async {
final prefs = await SharedPreferences.getInstance();
String value = prefs.getString(key) ?? "NULL";
return value;
}
store in variable callin getValue
void initState() {
setState(() {
_blueUriInit = getValue("name");
});
super.initState();
}

How to store data in shared preference when retrieve from database firestore in flutter?

In my code at the home page fetch user name from firestore database and that's display nicely in UI. I want pass that name to shared preference function and store there and use that name in another pages also.
Code
home page code ( initstate and send name to saveNameToSharedPreferences() method )
#override
void initState() {
super.initState();
getData();
fetchName();
storeName();
}
void storeName() {
String displayName = '${user?.displayName}';
return displayName.saveNameToSharedPreferences();
}
SharedPreferences code
import 'package:shared_preferences/shared_preferences.dart';
String? _displayName;
String? get displayName => _displayName;
Future saveNameToSharedPreferences() async {
final SharedPreferences sn = await SharedPreferences.getInstance();
await sn.setString('displayName', _displayName!);
}
Future getNameFromSharedPreferences() async {
final SharedPreferences sn = await SharedPreferences.getInstance();
_displayName = sn.getString('displayName');
}
How to solve this ?
You are calling function as an extension. Try to pass parameter instead.
Make the following changes
Future saveNameToSharedPreferences(String displayName) async {
final SharedPreferences sn = await SharedPreferences.getInstance();
await sn.setString('displayName', displayName);
}
And call it as
void storeName() {
String displayName = '${user?.displayName}';
saveNameToSharedPreferences(displayName);
}

How can I make this work? My shared preferences seem to store the wrong value?

I have these functions to set, remove etc variables I want globally available. I fear it might be just a small mistake on my behalf. What can I do to fix this?
static setUserId(userId, value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setInt('userId', value);
}
static getUserId() async {
final prefs = await SharedPreferences.getInstance();
prefs.getInt('userId') ?? 0;
}
static removeUserId() async {
final prefs = await SharedPreferences.getInstance();
await prefs.remove('userId');
}
static removeAllPreferences() async {
final prefs = await SharedPreferences.getInstance();
await prefs.clear();
}
}
var userId = user.id;
var value = userId?.toInt();
AccountPreferences.setUserId('userId', value);
var companyId = user.role![0].companyId;
var test = AccountPreferences.getUserId();
print(test); ```
When I run the code above all I print out is an instance of Future<dynamic>?
What am I doing wrong?
You should also await when getting the value and for that, you should declare the function getUserId() as Future and also return the function value like this:
static Future<int> getUserId() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getInt('userId') ?? 0;
}
var test = await AccountPreferences.getUserId(); // await inside here too, where you call it

Flutter shared preference code optimization suggestion?

I am using the shared_preferences package. https://pub.dev/packages/shared_preferences/example
In my repository class, for each function, I am doing this to get the instance.
SharedPreferences prefs = await SharedPreferences.getInstance();
class AuthenticationRepository {
Future<dynamic> logIn({required String email, required String password}) async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <--------
....
prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
SharedPreferences prefs = await SharedPreferences.getInstance(); <---------
prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
I am just wondering if this is initiating a new sharedPreference object or as the function implies, we are only getting the same instance?
Is there a better way to create the instance once, maybe as a class variable like below?
class AuthenticationRepository {
SharedPreferences prefs = await SharedPreferences.getInstance();
Future<dynamic> logIn({required String email, required String password}) async {
....
this.prefs.clear();
prefs.setString('user', encodedUser);
}
Future<String> logOut() async {
this.prefs.clear();
if(prefs.containsKey('user')){
return 'failed';
}else{
return 'cleared';
}
}
}
Please advice, thanks in advance :)
Yes, you can get the same instance. In the shared_preference.dart file, there is a static value _completer. Here is getInstance() function. You can see the if (_completer == null), and it immediately returns a value when the _completer had been initialized.
static Completer<SharedPreferences>? _completer;
...
static Future<SharedPreferences> getInstance() async {
if (_completer == null) {
final completer = Completer<SharedPreferences>();
try {
final Map<String, Object> preferencesMap =
await _getSharedPreferencesMap();
completer.complete(SharedPreferences._(preferencesMap));
} on Exception catch (e) {
// If there's an error, explicitly return the future with an error.
// then set the completer to null so we can retry.
completer.completeError(e);
final Future<SharedPreferences> sharedPrefsFuture = completer.future;
_completer = null;
return sharedPrefsFuture;
}
_completer = completer;
}
return _completer!.future;
}
I think it is a better way to use the getInstance() function not to create another class.

Flutter Null Safe Config Class with shared_preferences

In flutter 1.x, I implemented a Config class using the Flutter shared_preferences package; the code looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken;
SharedPreferences prefs;
Config._internal() {
loadData();
}
void loadData() async {
prefs = await SharedPreferences.getInstance();
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
prefs.setString(key, value);
}
}
I’m creating a new project in Flutter 2.x and trying to use the same code, but due to changes associated with null safety I’m having some difficulty getting the updated code just right.
The updated documentation for the package says to initialize the _prefs object like this:
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Then create a local prefs object using:
final SharedPreferences prefs = await _prefs;
This is fine, but I don’t want to have to make every class method that uses shared_preferences async then recreate the variable. At the same time I can’t create it as a class variable without initializing it first. Can someone please show me a cleaner way to do this, or do I just have to redeclare it every time I use it?
Also, how do I initialize the config object in my other classes? In my 1.x code, I would just do this:
final Config config = new Config();
then start accessing the properties of the config object. How do I initialize it with all of the async code in the class now?
Here’s where the updated code is today:
import 'package:shared_preferences/shared_preferences.dart';
import '../models/device.dart';
class Config {
static final Config _config = Config._internal();
factory Config() => _config;
final accessTokenKey = 'accessToken';
String _accessToken = '';
Future<SharedPreferences> _prefs = SharedPreferences.getInstance();
Config._internal() {
print('Config constructor');
loadData();
}
Future<void> loadData() async {
final SharedPreferences prefs = await _prefs;
_accessToken = prefs.getString(accessTokenKey) ?? '';
}
String get accessToken {
return _accessToken;
}
set accessToken(String accessToken) {
_accessToken = accessToken;
_saveString(accessTokenKey, accessToken);
}
_saveString(String key, String value, {String printValue = ''}) {
String printVal = printValue.length > 0 ? printValue : value;
print('Config: _saveString("$key", "$printVal")');
final SharedPreferences prefs = await _prefs;
prefs.setString(key, value);
}
}
You can get instance of SharedPreferences as static field in init method:
static SharedPreferences? _prefs; //or: static late SharedPreferences _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
And call init() somewhere like in build() method of first widget run, for once.Now you can use _prefs everywhere as you want.
If I want to show you a complete class to use SharedPreferences, it looks like this:
import 'package:shared_preferences/shared_preferences.dart';
class SharedPreferencesRepository {
static SharedPreferences? _prefs;
static init() async {
_prefs = await SharedPreferences.getInstance();
}
static putInteger(String key, int value) {
if (_prefs != null) _prefs!.setInt(key, value);
}
static int getInteger(String key) {
return _prefs == null ? 0 : _prefs!.getInt(key) ?? 0;
}
static putString(String key, String value) {
if (_prefs != null) _prefs!.setString(key, value);
}
static String getString(String key) {
return _prefs == null ? 'DEFAULT_VALUE' : _prefs!.getString(key) ?? "";
}
static putBool(String key, bool value) {
if (_prefs != null) _prefs!.setBool(key, value);
}
static bool getBool(String key) {
return _prefs == null ? false : _prefs!.getBool(key) ?? false;
}
}
I hope this useful for you.
If you need to wait for some async work to finish before getting an instance of a class, consider using a static method (not a factory constructor, since constructors must always return the base type).
You can use late fields to allow them to be non-null before you initialize them:
class Config {
late String _accessToken;
String get accessToken => _accessToken;
Config._(); // private constructor to prevent accidental creation
static Future<Config> create() async {
final config = Config();
final preferences = await SharedPreferences.getInstance();
config._accessToken = await preferences.getString('<your key>');
return config;
}
}
If you want to make sure this is initialized before running your app, you can initialize it in your main() method before you call runApp() to give control to the Flutter framework:
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); // make sure all plugins loaded etc.
final config = await Config.create();
print(config.accessToken);
runApp(MyApp());
}