Store data model into Flutter Secure Storage - flutter

How do we store model data to flutter secure storage... or does it supports it?
I have a model like this... I load data from my API to this model... once I have data, I wanted to save it to the flutter secure storage and vice versa(load entire data to model from flutter secure storage)...
class MyUserModel {
MyUserModel({
this.authKey,
this.city,
this.contact,
this.email,
this.isContact,
this.userId,
});
String authKey;
String city;
String contact;
dynamic email;
bool isContact;
int userId;
}
Of course, I know we can read and write data like below... I am just checking if there is a way where we can directly write it from the model...
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
// Create storage
final storage = new FlutterSecureStorage();
// Read value
String value = await storage.read(key: key);
// Write value
await storage.write(key: key, value: value);
I saw hive supports this feature out of the box but I realized that it takes little time (2-3 sec) for initialization also Author is working on an alternative to hive due to two major blocks... the new database called Isar which clears all roadblock but it is still in development...
if it is possible then please share the sample code...

to save the object:
convert your object to map with toMap() method
serialize your map to string with serialize(...) method
save the string into secure storage
for restoring your object:
deserialize secure storage string to a map with deserialize(...) method
use fromJson() method to get your object
by that, you will serialize your data into a string and save and retrieve them.
class MyUserModel {
String authKey;
String city;
String contact;
dynamic email;
bool isContact;
int userId;
MyUserModel({
required this.authKey,
required this.city,
required this.contact,
required this.email,
required this.isContact,
required this.userId,
});
factory MyUserModel.fromJson(Map<String, dynamic> jsonData) =>
MyUserModel(
authKey: jsonData['auth_key'],
city: jsonData['city'],
contact: jsonData['contact'],
email: jsonData['email'],
isContact: jsonData['is_contact'] == '1',
userId: jsonData['user_id'],
);
}
static Map<String, dynamic> toMap(MyUserModel model) =>
<String, dynamic> {
'auth_key': model.authKey,
'city': model.city,
'contact': model.contact,
'email': model.email,
'is_contact': model.isContact ? '1' : '0',
'user_id': model.userId,
};
static String serialize(MyUserModel model) =>
json.encode(MyUserModel.toMap(model));
static MyUserModel deserialize(String json) =>
MyUserModel.fromJson(jsonDecode(json));
}
Usage:
final FlutterSecureStorage storage = FlutterSecureStorage();
await storage.write(key: key, value: MyUserModel.serialize(model));
MyUserModel model = MyUserModel.deserialize(await storage.read(key: key));

You can do this by encoding your Model into json saving it in Secure Storage and then decode the json and get the model back.
// Saving model into Storage
static Future<void> setMyUserModel(MyUserModel user) async {
await const FlutterSecureStorage().write(
key: 'user', value: user.toRawJson());
}
// Getting model from storage
static Future<MyUserModel> getMyUserModel() async {
return MyUserModel.fromRawJson(
await const FlutterSecureStorage().read(key: 'user') ??
'{}');
}
And of course you need to implement fromRawJson() and toRawJson() inside your model.
Sorry for the late reply :P.

To do this, encode your model into JSON and store it in secure storage. Later, decode the JSON to retrieve the model.
final storage = FlutterSecureStorage();
static const String modelData = "modelData";
// Saving model into Storage
static Future<void> setModel(user) async {
final jsonDataEncoded = jsonEncode(jsonData);
await storage.write(key: modelData , value: jsonDataEncoded);
}
//call this when you want to store your data to secured storage
ClassName.setModel(decodedResponse);//decode your json and send it here
//to read data from the local storage || secured storage
static Future<MyModel> getDataFromLocalStorage() async {
final jsonModel = await storage.read(key: modelData);
final jsonData = jsonDecode(jsonModel.toString());
final items = List.from(jsonData);
dataList = items.map((e) => MyModel.fromJson(e)).toList();
return dataList;
}

Related

How to access token saved as a variable in different dart page in a secured storage

I have saved a token as a variable in flutter secure storage in a file called login_response_model.dart and I am trying to access it in the home screen, but I keep getting error as undefined name:
Here is the login_response_model.dart:
class LoginResponse {
dynamic? key;
List<dynamic>? non_field_errors;
LoginResponse({this.key, this.non_field_errors});
factory LoginResponse.fromJson(mapOfBody) {
return LoginResponse(
key: mapOfBody['key'],
non_field_errors: mapOfBody['non_field_errors'],
);
}
}
LoginResponseModel loginResponseJson(String str) =>
LoginResponseModel.fromJson(json.decode(str));
class LoginResponseModel {
dynamic? key;
List<dynamic>? non_field_errors;
LoginResponseModel({this.key, this.non_field_errors});
LoginResponseModel.fromJson(mapOfBody) {
key:
mapOfBody['key'];
non_field_errors:
mapOfBody['non_field_errors'];
print(mapOfBody['key']);
// Create storage
final storage = new FlutterSecureStorage();
// Write value
storage.write(key: 'Token', value: mapOfBody['key']);
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['key'] = key;
_data['non_field_errors'] = non_field_errors;
return _data;
}
}
class Data {
Data({
required this.username,
required this.email,
required this.date,
required this.id,
required this.key,
});
late final String username;
late final String email;
late final String date;
late final String id;
late final String key;
Data.fromJson(Map<String, dynamic> json) {
username = json['username'];
email = json['email'];
date = json['date'];
id = json['id'];
key = json['key'];
}
Map<String, dynamic> toJson() {
final _data = <String, dynamic>{};
_data['username'] = username;
_data['email'] = email;
_data['date'] = date;
_data['id'] = id;
_data['key'] = key;
return _data;
}
}
Here is the homescreen:
Future<User> fetchUser() async {
var url = Uri.parse(Config.apiURL + Config.userProfileAPI);
var value = storage.read(key: 'Token');
My question:
How can I access the token saved in a secured storage into the home screen?
Is there an easier and more secure way to access the token saved other than the way I have arranged it?
Use can you SharedPreference & save token here. Each time you want to use, you just need to:
SharedPreferences prefs = await SharedPreferences.getInstance();
String token = prefs.getString("key_token") ?? "";
However, each time you want to use it, you again need to use async function & wait until it give you saved token. Although it doesn't take so much time & space but we need to notice this. You can init a global variable to save token whenever open app or login success. Learn more about Singleton.
Example in my app: data_instance.dart
class DataInstance {
static DataInstance _instance = new DataInstance.internal();
DataInstance.internal();
factory DataInstance() => _instance;
late String accessToken;
initPreference() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
DataInstance().accessToken = prefs.getString("key_token_haha")?? '';
}
}
In main.dart:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await DataInstance().initPreference();
runApp(const MyApp());
}

Firebase Realtime database serialization in Flutter

I'm struggling a bit with getting data I push to Firebase Realtime DB in Flutter.
I'm using this code to push data to FB:
DatabaseReference newPostRef = news_dbRef.push();
final newKey = news_dbRef.child('News').push().key;
newPostRef.set({
"timestamp": timestamp,
"content": content_u,
"title": title_u,
"imgURL": imageUrl_u.substring(0,imageUrl_u.lastIndexOf('?')),
"fileURL": fileUrl_u.substring(0,fileUrl_u.lastIndexOf('?')),
"user": _user
});
so it creates a desired object in Firebase like this:
screenshot from Firebase
Now when I'm trying to get this data to my app, I'm having issues with proper serialization with it.
This is what I'm doing:
DatabaseReference newsCountRef =
FirebaseDatabase.instance.ref().child('News');
newsCountRef.onValue.listen((DatabaseEvent event) {
var data = event.snapshot.value;
String encoded = jsonEncode(data);
Map<String, dynamic> postslist = jsonDecode(encoded);
var somelist = postslist.entries.map((e) => TestNewsModel(e.key, e.value)).toList();
so it brings me to a stage that I have a list... but cannot read values for each line.
Do you have any ideas what I'm missing? Here's a class I'm using for serialization:
class TestNewsModel {
String recordid;
dynamic fields;
TestNewsModel(this.recordid, this.fields);
String toString() {
return '{ ${this.recordid}, ${this.fields} }';
}
}
class Field {
String timestamp;
String content;
String title;
String imgURL;
String fileURL;
String user;
Field({
required this.timestamp,
required this.content,
required this.title,
required this.imgURL,
required this.fileURL,
required this.user,
});
String toString() {
return '{ ${this.timestamp}, ${this.content}, ${this.title}, ${this.imgURL}, ${this.fileURL}, ${this.user} }';
}}
Would recommend creating a .fromDoc method in your class, similar to how you would create a .fromJson method.
Heres an example from one of my projects, this way you can avoid encoding and decoding.
///Creates a [Patient] from the information from a single firestore doc.
factory Patient.fromDoc(doc) {
return Patient(
doc.data()['email'],
doc.data()['forename'],
doc.data()['surname'],
doc.data()['hospitalNum'].toString(),
doc.id,
);
}

How can we save the data from a complex object in Firebase Firestore in Flutter?

I use complex objects to manage data in my app. For example I have an object defined by the "Defi class" (meaning Challenge in French).
Here is the Defi class :
class Defi {
final int modeApprentissage;
final DateTime date;
final double score;
final int seconds;
final List mots;
final List erreurs;
final List listes;
final int langue;
final int type;
final int typeMots1;
final int typeMots2;
const Defi(
{required this.modeApprentissage,
required this.date,
required this.type,
required this.langue,
required this.typeMots1,
required this.typeMots2,
required this.score,
required this.seconds,
required this.listes,
required this.mots,
required this.erreurs});
}
I have a LIST of Defi that I would like to save on FIREBASE FIRESTORE.
My question : Is it absolutely necessary to transform my list of Defi into a map to save it on Firestore ? Or is there another way ?
Here is how I do it :
List _defisMap = [];
for (Defi def in _defis) {
_defisMap.add({
'modeApprentissage': def.modeApprentissage,
'type': def.type,
'langue': def.langue,
'typeMots1': def.typeMots1,
'typeMots2': def.typeMots2,
'date': def.date,
'score': def.score,
'seconds': def.seconds,
'listes': def.listes,
'mots': def.mots,
'erreurs': def.erreurs,
});
}
if (await _connectivity.checkConnectivity() != ConnectivityResult.none) {
await FirebaseFirestore.instance
.collection('familyAccounts')
.doc(_accountEmail)
.update({
'defis': _defisMap,
});
I read in some article that in classes such as Defi, I could add a "factory" constructor ? Does this have anything to do with what I'd like to do ?
I created another class :
class Classes {
final String code;
final String nom;
const Classes({
required this.code,
required this.nom,
});
Map<String, dynamic> toJson() => {
'code': code,
'nom': nom,
};
factory Classes.fromMap(Map data) => Classes(
code: data['code'] ?? '',
nom: data['nom'] ?? '',
);
}
I save a list of Classes on Firestore. No problem.
But to retrieve this list : I must go from the list of maps that is on Firestore to a list of "Classes". And I just can't get the syntax right !
Here is my code :
final DocumentSnapshot<Map<String, dynamic>> docInfo =
await FirebaseFirestore.instance
.collection('familyAccounts')
.doc(_accountEmail)
.get();
_typeCompte =
docInfo['typeCompte'] == 'prof' ? TypeCompte.prof : TypeCompte.eleve;
_userId = docInfo['userId'];
_linked = docInfo['linked'];
_name = docInfo['name'];
_avatar = docInfo['avatar'];
_classe = docInfo['classe'];
_classeCode = docInfo['classeCode'];
_country = docInfo['country'];
_region = docInfo['region'];
docInfo['langue'] == 'french'
? _selectedIoLanguage = Language.french
: _selectedIoLanguage = Language.english;
_teacherCode = docInfo['teacherCode'];
_indexList = docInfo['indexList'];
_nbrList = docInfo['nbrList'];
_dateCreationCompte = docInfo['dateCreation'].toDate();
_defiTemp = docInfo['defis'].toList();
if (_typeCompte == TypeCompte.prof) {
_etablissement = docInfo['etablissement'];
_mesClasses = docInfo['mesClasses'];
(_mesClasses should be a list of "Classes").
I sense it should be some kind of xxx.map() etc.... but I don't master this syntax.
You need to create toJson method to set as a map of your list. If you have a list of Defi class. You can send it to map.
class Defi {
final int modeApprentissage;
final DateTime date;
final double score;
final int seconds;
final List mots;
final List erreurs;
final List listes;
final int langue;
final int type;
final int typeMots1;
final int typeMots2;
const Defi(
{required this.modeApprentissage,
required this.date,
required this.type,
required this.langue,
required this.typeMots1,
required this.typeMots2,
required this.score,
required this.seconds,
required this.listes,
required this.mots,
required this.erreurs});
Map<String, dynamic> toJson() => {
'modeApprentissage': modeApprentissage,
'type': type,
'langue': langue,
'typeMots1': typeMots1,
'typeMots2': typeMots2,
'date': date,
'score': score,
'seconds': seconds,
'listes': listes,
'mots': mots,
'erreurs': erreurs,
};
}
Your list name is should be List<Defi> _defis then map it with toJson. var jsonMap = _defisMap.map((e) => e.toJson()).toList();
var jsonMap = _defis.map((e) => e.toJson()).toList();
if (await _connectivity.checkConnectivity() != ConnectivityResult.none) {
await FirebaseFirestore.instance
.collection('familyAccounts')
.doc(_accountEmail)
.update({
'defis': jsonMap,
});
You can also Map it from api call with fromJson method. Here is the way. Add this to your Defi class.
factory Defi.fromJson(Map<String, dynamic> json) {
return Defi(
modeApprentissage: json['modeApprentissage'],
type: json['type'],
langue: json['langue'],
typeMots1: json['typeMots1'],
typeMots2: json['typeMots2'],
date: json['date'],
score: json['score'],
seconds: json['seconds'],
listes: json['listes'],
mots: json['mots'],
erreurs: json['erreurs'],);
}
And when you call api you need to call that function.
final Map<String, dynamic> jsonResult = json.decode(response.body);
//if it is a list returning from api. You can change variables what you got from api.
(jsonResult as List<dynamic>)
.map((data) => Defi.fromJson(data))
.toList();
// if it is not list
Defi.fromJson(jsonResult);
Yes, it's absolutely necessary to transform into a map. Firestore is a document store, the definition of a document is a json object, which is represented by a map in Dart. BUT, you don't have to do it "manually". Most of us use packages to do the mapping for us.
Freezed or JsonSerializable and then we simply call .toJson and pass that to the function. In addition to that Firestore now supports the .withConverter function so you don't have to do any conversion if you know what type the collection is storing.
So if you know the type of a collection is Defi you can do
final defyCollection = FirebaseFirestore.instance.collection('defis').withConverter<Defi>(
fromFirestore: (snapshot, _) {
final data = snapshot.data()!;
data['id'] = snapshot.id;
return Defi.fromJson(data);
},
toFirestore: (object, _) => object.toJson(),
)
This way you can simply use the defyCollection and the data property or function will be typed to your type.

Flutter & Shared Preferences: How do i save and read back a List<Object> using setStringList? Also, how do i remove a specific object from the List?

class Restaurant {
Restaurant({
required this.name,
required this.description,
required this.address,
required this.imageUrl,
});
Restaurant.fromJson(Map<String, Object?> json)
: this(
name: json['name']! as String,
description: json['description']! as String,
address: json['address']! as String,
imageUrl: json['imageUrl'] as String,
);
final String name;
final String description;
final String address;
final String imageUrl;
Map<String, Object?> toJson() {
return {
'name': name,
'description': description,
'address': address,
'imageUrl': imageUrl,
};
}
}
I get the list of restaurants from Firebase and when the user clicks Favorite Icon, I want to save that restaurant locally and retrieve the list of favorite restaurants and show it on another page. I know I can do it on Firebase directly but for now, I want it saved locally.
I am looking forward to hearing from all of you. Thank you.
Like this
List<Restaurant> restaturants = [Restuarant(...),Restuarant(...)];
List<String> encodedRestaturants = restaturants.map((res)=>json.encode(res.toJson())).toList();
//to write
prefs.setStringList("restaturants",encodedRestaturants);
//to read
List<String> decodedRestaturantsString = prefs.getStringList("restaturants");
List<Restaurant> decodedRestaturants = decodedRestaturantsString.map((res)=>Restaturant.fromJson(json.decode(res))).toList();
Instead using SharedPreferences setStringList, you can use setString.
Here excerpt using setStringList:
// sample restaurants.
List<Restaurant> favoriteRestos = [resto1, resto2, resto3];
List<String> favorites = [];
// Generate json for each restaurant
for(var resto in favoriteRestos) {
var json = jsonEncode(resto);
favorites.add(json);
}
// Obtain shared preferences.
final prefs = await SharedPreferences.getInstance();
// saving
await prefs.setStringList('favorites', favorites);
// Reading part
List<String> jsonRestos = prefs.getStringList('favorites')??[];
List<Restaurant> resFavorites = [];
for(var jsonResto in jsonRestos) {
var map = jsonDecode(jsonResto);
var resto = Restaurant.fromJson(map);
resFavorites.add(resto);
}
For setString:
List<Restaurant> favoriteRestos = [resto1, resto2, resto3];
var json = jsonEncode(favoriteRestos);
// Obtain shared preferences.
final prefs = await SharedPreferences.getInstance();
// saving
await prefs.setString('favorites', json);
// reading
var resJson = prefs.getString('favorites')??'';
var parsedJson = jsonDecode(resJson);
List<Restaurant> items = List<Restaurant>.from(parsedJson.map((i) => Restaurant.fromJson(i)));
Note:
You need to update your Restaurant class. See https://docs.flutter.dev/development/data-and-backend/json#serializing-json-inside-model-classes

how to save list<object> in flutter

List<Data> datas = [];
Future<List<Data>> getData() async {
final response =
await http.get('https://iptv-org.github.io/iptv/index.country.m3u');
final m3u = await M3uParser.parse(response.body);
for (final entry in m3u) {
Data data = Data(entry.title, entry.attributes['tvg-logo'], entry.link,
entry.attributes['tvg-language'], 'false');
datas.add(data);
}
return datas;
}
class Data {
String title;
String logo;
String url;
String language;
String isFavorite = 'false';
Data(this.title, this.logo, this.url, this.language, this.isFavorite);
}
Everytime I get the data from the url, then how I gonna save the list of Data object? Can I save the data using sharedPref?
for complicated data its recommended to use the database. you can use SqfLite package as a database. but u can also use shared Preferences too. for creating Model use quicktype.io it will create your model with several useful methods such as toJson and fromJson. it will also generate fromRawJson and toRawJson. these two methods work with String. you can convert your model to string and stored to SharedPreferences and when you need it again take it and convert it to model.