Store data as an object in shared preferences in flutter - flutter

I want to store an object in shared preferences which contains some fields in it like name, age, phone number etc. I don't know how to store an object in shared preferences in flutter.

You can Store an object in shared preferences as Below:
SharedPreferences shared_User = await SharedPreferences.getInstance();
Map decode_options = jsonDecode(jsonString);
String user = jsonEncode(User.fromJson(decode_options));
shared_User.setString('user', user);
SharedPreferences shared_User = await SharedPreferences.getInstance();
Map userMap = jsonDecode(shared_User.getString('user'));
var user = User.fromJson(userMap);
class User {
final String name;
final String age;
User({this.name, this.age});
factory User.fromJson(Map<String, dynamic> parsedJson) {
return new User(
name: parsedJson['name'] ?? "",
age: parsedJson['age'] ?? "");
}
Map<String, dynamic> toJson() {
return {
"name": this.name,
"age": this.age
};
}
}

After searching a lot of articles here you are
For saving data to SharedPreferences instance, object must be converted to JSON:
SharedPreferences prefs = await SharedPreferences.getInstance();
Map<String, dynamic> user = {'Username':'tom','Password':'pass#123'};
bool result = await prefs.setString('user', jsonEncode(user));
For getting data from SharedPreferences instance, object must converted from JSON:
String userPref = prefs.getString('user');
Map<String,dynamic> userMap = jsonDecode(userPref) as Map<String, dynamic>;

To Save the object to Shared Preferences
SharedPreferences pref = await SharedPreferences.getInstance();
Map json = jsonDecode(jsonString);
String user = jsonEncode(UserModel.fromJson(json));
pref.setString('userData', user);
To Fetch the object from Shared Preferences
SharedPreferences pref = await SharedPreferences.getInstance();
Map json = jsonDecode(pref.getString('userData'));
var user = UserModel.fromJson(json);
You will need to import below mentioned packages
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
Easiest way to create Model
Follow this answer -> https://stackoverflow.com/a/58708634/9236994

You need to serialize it to JSON before saving and deserialize after reading
See https://flutter.io/docs/development/data-and-backend/json for details

When Getting Data from the API and Saving it Into Sharepreference
Future<UserDetails> UserInfo({String sesscode, regno}) async{
await Future.delayed(Duration(seconds: 1));
SharedPreferences preferences = await SharedPreferences.getInstance();
var map = new Map<String, String>();
map["sesscode"] = sesscode;
map["regno"] = regno;
var response = await http.post(Base_URL().user_info, body: map);
Map decodedata = json.decode(response.body);
if(decodedata != null){
String user = jsonEncode(UserDetails.fromJson(decodedata));
preferences.setString(SharePrefName.infoPref, user);
return UserDetails.fromJson(decodedata);
}
return null;
}
I Create A function for Getting the Details
You can call this function anywhere in your App
Future<UserDetails> getSavedInfo()async{
SharedPreferences preferences = await SharedPreferences.getInstance();
Map userMap = jsonDecode(preferences.getString(SharePrefName.infoPref));
UserDetails user = UserDetails.fromJson(userMap);
return user;
}
Now, Am calling it inside a Class to get username
Future<UserDetails> usd = getSavedInfo();
usd.then((value){
print(value.surname);
});

SharePreferences Handler
I have created a LocalStorageRepository class, that is responsible to handle local data using SharedPreferences.
The class is dynamic and can work with any type of data (int, double, bool, String, and Object) using generics and JSON decoding and encoding.
In order to prevent pron errors, I added the LocalStorageKeys enum to handle the supported keys. Feel free to add more keys to that enum.
enum LocalStorageKeys { tutorialCompleted, user }
#singleton
class LocalStorageRepository {
const LocalStorageRepository(SharedPreferences prefs) : _prefs = prefs;
final SharedPreferences _prefs;
bool keyExists(String key) => _prefs.containsKey(key);
T? getValue<T>(
LocalStorageKeys key, [
T Function(Map<String, dynamic>)? fromJson,
]) {
switch (T) {
case int:
return _prefs.getInt(key.name) as T?;
case double:
return _prefs.getDouble(key.name) as T?;
case String:
return _prefs.getString(key.name) as T?;
case bool:
return _prefs.getBool(key.name) as T?;
default:
assert(fromJson != null, 'fromJson must be provided for Object values');
if (fromJson != null) {
final stringObject = _prefs.getString(key.name);
if (stringObject == null) return null;
final jsonObject = jsonDecode(stringObject) as Map<String, dynamic>;
return fromJson(jsonObject);
}
}
return null;
}
void setValue<T>(LocalStorageKeys key, T value) {
switch (T) {
case int:
_prefs.setInt(key.name, value as int);
break;
case double:
_prefs.setDouble(key.name, value as double);
break;
case String:
_prefs.setString(key.name, value as String);
break;
case bool:
_prefs.setBool(key.name, value as bool);
break;
default:
assert(
value is Map<String, dynamic>,
'value must be int, double, String, bool or Map<String, dynamic>',
);
final stringObject = jsonEncode(value);
_prefs.setString(key.name, stringObject);
}
}
}
In case you want to get an Object value from LocalStorageRepository, you will need to provide its fromJson decoder.
final user = _localStorage.getValue(LocalStorageKeys.user, User.fromJson);
Hope that hence example will help others out there.
Feel free to edit this question and suggest any changes.

If you are getting you data from an API, what you initially get from an API endpoint is a String so you can store the data as a raw String and when you need it you can deserialize it and use where you want to use it
https://gist.github.com/k1ycee/33bb7e51dac81093f949bbd30d7d0dc9
Something like this, the drawback I feel is that if the JSON string data is much might not be advisable to store all the string rather deserialize it and take the ones you deem necessary.

Related

Flutter Exception Handling Show error message when the user enteres incorrect city name

I am using one of openweathermap to get the latitude and longitude based on a city name.
Whenever the user enters an invalid city name, this is the response from the api.
How can I catch this and show an error message to the user.
This is the function that makes the api call.
Constants myConstaints = Constants();
Future<CityInfo> gettingCityData(String cityName) async {
var url = Uri.parse(
'https://api.openweathermap.org/geo/1.0/direct?q=$cityName&limit=1&appid=${myConstaints.apiKey}');
var response = await http.get(url);
if (response.statusCode == 200) {
var i = CityInfo.fromJson(jsonDecode(response.body));
return i;
} else
throw Exception('error');
}
CityInfo class and its constructor
class CityInfo {
String name;
double lat;
double long;
CityInfo.fromJson(List<dynamic> json)
: name = json[0]['name'],
lat = json[0]['lat'].toDouble(),
long = json[0]['lon'].toDouble();
}
Provider
Future<void> cityName(String cityName) async {
cityInfo = await gettingCityData(cityName);
notifyListeners();
}
The API is returning a list of cities. It may return an empty list.
First of all, the CityInfo.fromJson should not take a list as input. It should rather only focus on converting a CityInfo JSON object to a CityInfo object.
class CityInfo {
String name;
double lat;
double long;
CityInfo.fromJson(Map<String, dynamic> json)
: name = json['name'],
lat = json['lat'].toDouble(),
long = json['lon'].toDouble();
}
Now, notice how CityInfo can be null so your future should return a nullable CityInfo
Future<CityInfo?> gettingCityData(String cityName)
Now to handle the request,
Future<CityInfo?> gettingCityData(String cityName) async {
final url = Uri.parse(
'https://api.openweathermap.org/geo/1.0/direct?q=$cityName&limit=1&appid=${myConstaints.apiKey}');
final response = await http.get(url);
if (response.statusCode == 200) {
final List<dynamic> data = jsonDecode(response.body);
if (data.isEmpty) return null; // List is empty.
final cityJson = data.first as Map<String, dynamic>;
return CityInfo.fromJson(cityJson);
} else
throw Exception('Error');
}
}
Now, the method can be called as,
Future<void> cityName(String cityName) async {
cityInfo = await gettingCityData(cityName);
if (cityInfo == null) {
// City was not found. Show some message here.
}
notifyListeners();
}

SharedPerference key returning null value in Flutter

I created a user model where I'm saving required data to save in sharedperference so that if user kill the app so I can get it from sharedperference. here is my user model looks like.
class UserModel {
String? token;
String? userId;
String? stripeId;
String? userName;
String? userEmailAddress;
UserModel({this.activeCardId,this.token,this.userId,this.stripeId,this.userName});
UserModel.fromJson(Map<String, dynamic> json) {
token = json['token'];
userId = json['user_id'];
stripeId = json['stripe_id'];
userName=json['fullname'];
userEmailAddress=json['email'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['token'] = token;
data['user_id'] = userId;
data['stripe_id'] = stripeId;
data['fullname']=userName;
data['email']=userEmailAddress;
return data;
}
}
and here is my sharedperference class code, i used provider state management for this
class UserViewModel with ChangeNotifier{
Future<bool> saveUser(UserModel user)async{
final SharedPreferences sp = await SharedPreferences.getInstance();
sp.setString('token', user.token.toString());
sp.setString('user_id',user.userId.toString());
sp.setString('stripe_id',user.stripeId.toString());
sp.setString('userName',user.userName.toString());
sp.setString('userEmailAddress',user.userEmailAddress.toString());
notifyListeners();
return true ;
}
Future<UserModel> getUser()async{
final SharedPreferences sp = await SharedPreferences.getInstance();
final String? token = sp.getString('token');
final String? userId = sp.getString('user_id');
final String? stripeId=sp.getString('stripe_id');
final String? userName=sp.getString('userName');
final String? userEmailAddress=sp.getString('userEmailAddress');
return UserModel(
token: token.toString(),
userId: userId.toString(),
stripeId: stripeId.toString(),
userName:userName.toString(),
userEmailAddress:userEmailAddress.toString(),
);
}
Future<bool> remove()async{
final SharedPreferences sp = await SharedPreferences.getInstance();
sp.remove('token');
return true;
}
}
and this is how i'm saving data which I get from Login API response and using this code on Login screen
final userPreference =Provider.of<UserViewModel>(context, listen: false);
userPreference.saveUser(UserModel(userId: value['data']['id'].toString()));
and this is how I'm getting data from sharedPrefernce, using this code of Drawer Widget class
Future<UserModel> getUserDate() => UserViewModel().getUser();
getUserDate().then((value)async{
setState(() {
GlobalVariables.token=value.token.toString();
});
});
PROBLEM
The problem is I need to save the stripe_id on sharedpreference so when user get logged in there is screen called Add Card when user click on button an API hits and on its response I'm getting stripe_id and saving it to sharedpereference same as i saved login response, data. But when I came back to Drawer Widget class it prints null value of token. It works fine when I'm not saving stripe_id on sharedpreference.
here is the code how I'm saving stripe_id
final userPreference =Provider.of<UserViewModel>(context,listen: false);
userPreference.saveUser(UserModel(stripe_id: createCard['id'].toString()));
But, when i do above code like this
final userPreference =Provider.of<UserViewModel>(context,listen: false);
userPreference.saveUser(UserModel(stripe_id: createCard['id'].toString()));
userPreference.saveUser(UserModel(token: "22424"));
I get the token value "22424",but I don't want to do it like this. My point is when the sharepreference data is null after saving other data on other key.
Kindly help where I'm doing wrong.
You're simply overriding with NULL values every time you're calling the saveUser() method with an User object with NULL values for it's properties.
You're passing an User object with selected values like stripe_id or token while passing other values NULL and then when you call the saveUser() method, you're saving only the passed values while saving others as NULL by default which get's saved too.
You should check for NULL value before saving each objet's property.
Update your saveUser method with this:
Future<bool> saveUser(UserModel user) async {
final SharedPreferences sp = await SharedPreferences.getInstance();
if (user.token != null) sp.setString('token', user.token.toString());
if (user.userId != null) sp.setString('user_id', user.userId.toString());
if (user.stripeId != null) sp.setString('stripe_id', user.stripeId.toString());
if (user.userName != null) sp.setString('userName', user.userName.toString());
if (user.userEmailAddress != null) sp.setString('userEmailAddress', user.userEmailAddress.toString());
notifyListeners();
return true;
}

How to save list data in SharedPreferences flutter

I make data connection with database to bring a list of data such as the following code:
var listDATA = [];
Future listDATAs() async {
api = '*************';
var response = await http.post(Uri.parse(api));
var responsebody = jsonDecode(response.body);
if (responsebody.length >0){
for (int i = 0; i < responsebody.length; i++) {
listDATA.add(responsebody[i]['name']+ ':' + responsebody[i]['image'].toString());
}
return responsebody;
}else{
}
}
How can I store listDATA in Shared Preferences I need to save name and image ? Then recall it to display after storage
It's preferred not to store non-primitive data types in SharedPreferences as it supports only primitive data types by default. But still there is a way to do it.
you can store the response body of your API call without decoding JSON to a String value.
// Obtain shared preferences.
final prefs = await SharedPreferences.getInstance();
// Save an String value to 'response' key.
await prefs.setString('response', response.body);
if you have response types of List, you can use setStringList method
await prefs.setStringList('items', <String>['Earth', 'Moon', 'Sun']);
in this way you can store list value in shared preference
static setListValue(String key, List< ProductsModel > value) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setString(key, jsonEncode(value.map((e) => e.toJson()).toList()));
}
her I make a standard method to store list values from any class by calling
setListValue('store_list', listData);
after that, you have to make a method for getting this list value
//getListValue
static Future<List<ProductsModel>?> getListValue(String key) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
final dataMap = jsonDecode(prefs.getString(key) ?? '[]') as
List<dynamic>;
return dataMap.map<ProductsModel>((item) {
return ProductsModel.fromJson(item);
}).toList();
}
after that, you can call this method like this
var listValue = await SPUtils.getListValue('store_list');
// for saving the list in shared preferences
final prefs = await SharedPreferences.getInstance();
prefs.setString("list",jsonEncode(listDATA));
// for getting the list from shared preferences
final prefs = await SharedPreferences.getInstance();
List listDATA = jsonDecode(prefs.get("list"));
You can follow those steps.
convert your object to map with toMap() method.
encode your map to string with encode() method.
Save the string to shared preferences.
final SharedPreferences prefs = await SharedPreferences.getInstance();
await prefs.setString('key', encodedData);
// Fetch and decode data
final String musicsString = await prefs.getString('musics_key');
Example :
import 'dart:convert';
void main() async {
final SharedPreferences prefs = await SharedPreferences.getInstance();
final String encodedData = Music.encode([
Music(id: 1, ...),
Music(id: 2, ...),
Music(id: 3, ...),
]);
await prefs.setString('musics_key', encodedData);
// Fetch and decode data final String musicsString = await prefs.getString('musics_key');
final List<Music> musics = Music.decode(musicsString); }
class Music {
final int id;
final String name,
size,
rating,
duration, img; bool favorite;
Music({
this.id,
this.rating,
this.size,
this.duration,
this.name,
this.img,
this.favorite, });
factory Music.fromJson(Map<String, dynamic> jsonData) {
return Music(
id: jsonData['id'],
rating: jsonData['rating'],
size: jsonData['size'],
duration: jsonData['duration'],
name: jsonData['name'],
img: jsonData['img'],
favorite: false,
); }
static Map<String, dynamic> toMap(Music music) => {
'id': music.id,
'rating': music.rating,
'size': music.size,
'duration': music.duration,
'name': music.name,
'img': music.img,
'favorite': music.favorite,
};
static String encode(List<Music> musics) => json.encode(
musics
.map<Map<String, dynamic>>((music) => Music.toMap(music))
.toList(),
);
static List<Music> decode(String musics) =>
(json.decode(musics) as List<dynamic>)
.map<Music>((item) => Music.fromJson(item))
.toList(); }

how can i store List<object> in sharedpreferences using provider?

so basically this is my provider, what i want is how can i store the model data coming from provider to a shared preferences, and then how to decode it to show it in a bookmark page?
class bookmark extends ChangeNotifier{
int _count = 0;
List<bookmarkModel> bookM = [];
void addCount(){
_count++;
notifyListeners();
}
void addItems(bookmarkModel i){
bookM.add(i);
notifyListeners();
}
int get count => _count;
List<bookmarkModel> get bookMList => bookM;
}
here is my model:
import 'package:flutter/cupertino.dart';
class bookmarkModel{
String title;
String subtitle;
int num;
bookmarkModel({this.title, this.subtitle, this.num});
bookmarkModel.fromJson(Map<String,dynamic> json) :
title = json['title'],
subtitle = json['sutitle'],
num = json['num'];
Map<String, dynamic> toJson()=>{
'title':title,
'subtitle':subtitle,
'num': num
};
}
SharedPreferences should only be used to store small and simple values. It's not meant to be used as a Database.
You can use sharedPreferences to store bool, String, int and simple Lists (not lists of objects or maps). As far as I know, it even cannot store doubles.
Try using a SQflite or Hive (No-SQL) to store more complex or extensive data locally.
You already have toJosn and fromJson ready to use, you just need to convert bookM to a map josnEnode() and the get it back using josnDecode().
try the code below:
void saveBookmark() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
final String List = jsonEncode(bookM, toEncodable: (c)=> c.toJson());
await prefs.setString('BookMarkList', List);
}
void loadBookMark() async{
SharedPreferences prefs = await SharedPreferences.getInstance();
final String saved = prefs.getString('BookMarkList');
final List<dynamic> decoded = jsonDecode(saved);
bookM = decoded.map((bookmark) => bookmarkModel.fromJson(bookmark));
}

Create Json Arrays and List with SharedPreferences

I have been using shared_preferences to create and write in Json Files. The Problem i facing is i dont know how to create a Json Array and a List in shared_preferences.
I want to save and read a Json List.
read(String key) async {
final prefs = await SharedPreferences.getInstance();
return json.decode(prefs.getString(key));
}
save(String key, value) async {
final prefs = await SharedPreferences.getInstance();
prefs.setString(key, json.encode(value));
}
remove(String key) async {
final prefs = await SharedPreferences.getInstance();
prefs.remove(key);
}
} ```
Example on DartPad.
Save a list to SharedPreferences with setStringList:
const String key = "users";
List<User> users = [User(name: "tester")];
List<String> jsonList = users.map((user) => user.toJson()).toList();
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setStringList(key, jsonList);
Read a list from SharedPreferences with getStringList:
jsonList = prefs.getStringList(key);
users = jsonList.map((json) => User.fromJson(json)).toList();
The user class with json convert: JSON and serialization
class User {
String name;
int age;
User({
this.name,
this.age,
});
factory User.fromJson(String str) => User.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory User.fromMap(Map<String, dynamic> json) => User(
name: json["name"],
age: json["age"],
);
Map<String, dynamic> toMap() => {
"name": name,
"age": age,
};
}
Just map your json array to List<String> and after you can use the setStringList function provided in shared_preferences.dart
/// Saves a list of strings [value] to persistent storage in the background.
///
/// If [value] is null, this is equivalent to calling [remove()] on the [key].
Future<bool> setStringList(String key, List<String> value) =>
_setValue('StringList', key, value);