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;
}
Related
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());
}
I am trying to populate 2 values in my user profile. The values are name and email. I have implemented a function getCurrentUser which uses firebase authentication to get the current signed in user. I then use the uid for the user to query my users database were the name and email is stored.
Inside the function, if I print current user I get the users instance returning with values name, email and isStudent. When running my app is m getting the following error in the console "Warning: Operand of null-aware operation '!' has type 'Map<String, dynamic>' which excludes null." I also get an error on the phone UI saying "LateInitializationError: Field 'name' has not been initialized"
Any advice on this is much appreciated!
I have attached the screenshot of the error and code snippet including variables,function and the variable call to display the values.
final _firestore = FirebaseFirestore.instance;
late User loggedInUser;
class MenuLecturerScreen extends StatefulWidget {
static const String id = 'menu_lecturer';
#override
_MenuLecturerScreenState createState() => _MenuLecturerScreenState();
}
class _MenuLecturerScreenState extends State<MenuLecturerScreen> {
final messageTextController = TextEditingController();
final _auth = FirebaseAuth.instance;
late String messageText;
//String name = 'Eoin Irwin';
//String email = 'irwin-e14#ulster.ac.uk';
late String name;
late String email;
#override
void initState() {
super.initState();
getCurrentUser();
}
void getCurrentUser() async {
try {
final user = _auth.currentUser;
await FirebaseFirestore.instance
.collection("users")
.doc(user?.uid)
.get()
.then((value) {
final Map<String, dynamic>? currentUser = value.data();
email = currentUser!['email'];
name = currentUser!['name'];
print(currentUser);
});
//print(FirebaseFirestore.instance.collection("users").doc(user?.uid).get());
if (user != null) {
loggedInUser = user;
print(loggedInUser.email);
}
} catch (e) {
print(e);
}
}
signOut() async {
await _auth.signOut();
Navigator.pushNamed(context, WelcomeScreen.id);
}
#override
Widget build(BuildContext context) {
var drawerHeader = UserAccountsDrawerHeader(
accountName: Text(name),
accountEmail: Text(email),
You are using a async method. And after getting data you are not using setState to update the UI.
await FirebaseFirestore.instance
.collection("users")
.doc(user?.uid)
.get()
.then((value) {
final Map<String, dynamic>? currentUser = value.data();
email = currentUser!['email'];
name = currentUser!['name'];
print(currentUser);
setState((){});
});
Also initially name, messageText and email are null. better use nullable data here
String? messageText;
String? name;
String? email;
Reading map can provide null, for safe case accept null data
email = currentUser?['email'];
name = currentUser?['name'];
And while using it do
Text(name??"")
Future takes some time to fetch data until then variables are null .
By adding a ? You mentioned that the map can be null but then if you added ! You are mentioning that is not null and has a value.. try adding ? Instead
final Map<String, dynamic>?currentUser = value.data();
email = currentUser?['email']??"";
name = currentUser?['name']??"";
print(currentUser);
Edit
String name = "";
String email = "";
I need to retrieve User Data from Firebase and use a builder to pass on the data to UI. When I run the apps, I method is called in on Null.
I tried many ways to call firebase data but I keep receive error message on provider or on calling the data NULL.
The error is most likely coming from the method _getProfileData() below.
_getProfileData(AuthNotifier authNotifier) async {
final uid = await Provider.of(context, listen: false).authNotifier.getCurrentUID();
await Provider.of(context, listen: false)
.collection('Users')
.document(uid)
.get().then((result) {
user.isAdmin = result.data['isAdmin'];
});
}
When I made the changes below by using Provider, another error appears with Provider not working.
final uid = await Provider.of<authNotifier>(context, listen: false).getCurrentUID();
I placed the getter in the API.
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
// GET UID
Future<String> getCurrentUID(User user, AuthNotifier authNotifier) async {
return (await _firebaseAuth.currentUser()).uid;
}
// GET CURRENT USER
Future getCurrentUser(User user, AuthNotifier authNotifier) async {
return await _firebaseAuth.currentUser();
}
Stream<String> get onAuthStateChanged => auth.onAuthStateChanged.map(
(FirebaseUser user) => user?.uid,
);
I structured User Data as below.
class User {
List favorites = [];
String documentID;
String displayName;
String email;
String password;
bool isAdmin;
User({
this.favorites,
this.documentID,
this.displayName,
this.email,
this.password,
this.isAdmin,
});
factory User.fromFirestore(DocumentSnapshot document) {
Map data = document.data;
return User(
favorites: data['favorite'] ?? [],
documentID: document.documentID,
displayName: data['displayName'] ?? '',
email: data['email'] ?? '',
isAdmin: data['isAdmin'] ?? false,
);
}
// get admin => null;
Map<String, dynamic> toMap() {
return {
'displayName': displayName,
'email': email,
'isAdmin': isAdmin,
};
}
}
I am working on Firebase real time databases in Flutter. I am storing user information and their corresponding data in database. My code is given below:
//User Auth Class
class UserAuth{
final String id;
final String email;
final String token;
UserAuth({#required this.id, #required this.email, #required this.token});
}
//User Details Class for storing data of corresponding user
class UserDetails{
final String userDetailsId;
final String name;
final String email;
UserDetails({#required this.userDetailsId,#required this.name, #required this.email});
}
//I am using this code to add userDetails in database.
List<UserDetails> _detailsList = [];
UserDetails _details;
Future<bool> addUserDetails(String username, String email) async {
_isLoading = true;
notifyListeners();
final Map<String, dynamic> userDetails = {
'username': username,
'email': email,
};
try {
final http.Response response = await http.post(
'https://intro-to-firebase-711d4.firebaseio.com/Users.json',
body: json.encode(userDetails));
if (response.statusCode != 200 && response.statusCode != 201) {
_isLoading = false;
notifyListeners();
return false;
}
final Map<String, dynamic> responseData = json.decode(response.body);
_details = UserDetails(
userDetailsId: responseData['name'], name: username, email: email);
_detailsList.add(_details);
_isLoading = false;
notifyListeners();
return true;
} catch (error) {
_isLoading = false;
notifyListeners();
return false;
}
}
Now I want to get the following highlighted ids of the user so how to fetch it. I am using scoped model as a state management in Flutter.
I did it with javascript so far but in the documentation it seems not taht different for flutter:
private void writeNewUser(String userId, String name, String email) {
User user = new User(name, email);
mDatabase.child("users").child(userId).setValue(user);
}
https://firebase.google.com/docs/database/android/read-and-write?authuser=0
This is the example shown in the docs. with .child('users') you can get the right section .child(userId) gives you access to the values.
All you have to do now is defining the variable userId
flutter
you can achieve this as follows
stream: FirebaseDatabase.instance
.reference()
.child('users')
.equalTo(userId)
.onValue,
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.