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,
};
}
}
Related
In my code when signup then should pass and save uid and email in saveDataToSharedPreferences() method. saveDataToSharedPreferences method also used for google signin then that's work nicely. But when signup using email password I want pass email and uid to same "saveDataToSharedPreferences() " method.(google signin saveDataToSharedPreferences method).
saveDataToSharedPreferences() method This has in SignInProvider class
class SignInProvider extends ChangeNotifier {
//instantiate of firebaseAuth, facebook and google
final FirebaseAuth firebaseAuth = FirebaseAuth.instance;
final GoogleSignIn googleSignIn = GoogleSignIn();
bool _isSignedIn = false;
bool get isSignedIn => _isSignedIn;
//hasError, errorCode, provider,uid,email,name,imageUrl
bool _hasError = false;
bool get hasError => _hasError;
String? _errorCode;
String? get errorCode => _errorCode;
String? _uid;
String? get uid => _uid;
String? _email;
String? get email => _email;
Future saveDataToSharedPreferences() async {
final SharedPreferences s = await SharedPreferences.getInstance();
await s.setString('email', _email!);
await s.setString('uid', _uid!);
notifyListeners();
}
Future getDataFromSharedPreferences() async {
final SharedPreferences s = await SharedPreferences.getInstance();
_email = s.getString('email');
_uid = s.getString('uid');
notifyListeners();
}
}
email password signup method (postDetailsToFirestore() method)
postDetailsToFirestore() async {
// calling our fireStore
//calling our user model
// sending these values
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
final sp = context.read<SignInProvider>();
UserModel userModel = UserModel();
if (user != null) {
//writing all the values
userModel.email = user?.email;
userModel.uid = user?.uid;
await firebaseFirestore
.collection("users")
.doc(user?.uid)
.set(userModel.toMap())
.then((value) => sp
.saveDataToSharedPreferences()
.then((value) => sp.setSignIn().then((value) {})));
Fluttertoast.showToast(msg: "Account created successfully ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
}
in postDetailsToFirestore() method added sp.saveDataToSharedPreferences() to pass uid and email to saveDataToSharedPreferences() in siginInProvider class.
email password signup has another model
class UserModel {
String? uid;
String? email;
String? isSignedIn;
UserModel({
this.uid,
this.email,
});
//receiving data from server
factory UserModel.formMap(map) {
return UserModel(
uid: map['uid'],
email: map['email'],
);
}
// sending data to server
Map<String, dynamic> toMap() {
return {
'uid': uid,
'email': email,
};
}
}
when added sp.saveDataToSharedPreferences() in postDetailsToFirestore() then show this error.
How pass uid and email to saveDataToSharedPreferences() from in signup method?
i am trying to send data to firestore and am following a tutorial on YouTube, this tutorial approach of posting data to the server is by creating "UserModel" class as follows:
class UserModel {
String? uid;
String? name;
String? email;
String? phone;
String? province;
String? dateOfBirth;
//parsing data to JSON
UserModel(
{this.uid,
this.name,
this.email,
this.phone,
this.province,
this.dateOfBirth});
//Access and fetching data from the server (cloud firestore)
factory UserModel.fromMap(map) {
return UserModel(
uid: map['uid'],
name: map['name'],
email: map['email'],
phone: map['phone'],
province: map['province'],
dateOfBirth: map['dateOfBirth'],
);
}
//Sending data to server (cloud firestore)
Map<String, dynamic> toMap() {
return {
'uid': uid,
'name': name,
'email': email,
'phone': phone,
'province': province,
'dateOfBirth': dateOfBirth,
};
}
}
Then in my registration form screen we create a method and declare an instance of the UserModel class as follows:
//Sign up method (when user clicks on sign up this method will be invoked)
void signUp(String email, String password) async {
if (_formKey.currentState!.validate()) {
await _auth
.createUserWithEmailAndPassword(email: email, password: password)
.then((value) => { postDataToFirestore();})
.catchError((e) {
Fluttertoast.showToast(msg: e!.message);
});
}
}
postDataToFirestore() async {
//Creating Instance of firestore from firebase
FirebaseFirestore firebaseFirestore = FirebaseFirestore.instance;
User? user = _auth.currentUser;
//Creating an instance of UserModel Class
UserModel userModel = UserModel();
//providing the fields values to the user model class
userModel.name = user!.nameController.text; // Error Here
await firebaseFirestore
.collection("users")
.doc(user.uid)
.set(userModel.toMap());
Fluttertoast.showToast(msg: "Account created successfully :) ");
Navigator.pushAndRemoveUntil(
(context),
MaterialPageRoute(builder: (context) => HomeScreen()),
(route) => false);
}
}
in the line where comment says "providing the fields values to the user model class" of this code the "user" is not accessing any "Controller" i created for the form fields my last search told me that this way is not used anymore, i will be thankful if anyone could provide the right way of posting data
Use
userModel.name = nameController.text;
You wrote user.namecontroller and the error says there is no name controller in user that you created
I am trying to add a shopping cart functionality to my app wherein the UserModel contains fields name,email,uid and cart. I have created an AuthController extends GetxController where I have created userModel isntance and then in setInitialScreen function I am binding the userModel to a function "listenToUser()" which is a Stream and recieves snapshot from firestore and maps it to the userModel. But on printing the userModel I see null being printed in the console meaning the data is not getting binded which is causing problems as i can't access the cart stored in the userModel.
Edit: I saw that I need to attach obs to userModel like: Rx<model.User>? userModel = model.User().obs; but there's a problem that all the fields in this model are required and how can i pass these field values when they have not yet been intialized.
Console output:
AuthController code
class AuthController extends GetxController {
static AuthController instance = Get.find();
late Rx<User?> _user;
Rx<model.User>? userModel;
#override
void onReady() {
super.onReady();
_user = Rx<User?>(firebaseAuth.currentUser);
_user.bindStream(firebaseAuth.authStateChanges());
ever(_user, _setInitialScreen);
}
_setInitialScreen(User? user) {
if (user == null) {
Get.offAll(() => LoginScreen());
} else {
userModel?.bindStream(listenToUser());
Get.offAll(() => const HomeScreen());
print(userModel); // PRINTING USER MODEL TO SEE IF ITS NULL
// userModel?.bindStream(listenToUser());
}
}
// registering the user
void registerUser(String username, String email, String password) async {
try {
if (username.isNotEmpty && email.isNotEmpty && password.isNotEmpty) {
// save our user to our auth and firebase firestore
UserCredential cred = await firebaseAuth.createUserWithEmailAndPassword(
email: email,
password: password,
);
model.User user = model.User(
name: username, email: email, uid: cred.user!.uid, cart: []);
await firestore
.collection('users')
.doc(cred.user!.uid)
.set(user.toJson());
} else {
Get.snackbar(
'Error Creating Account',
'Please enter all the fields',
);
}
} catch (e) {
Get.snackbar(
'Error Creating Account',
e.toString(),
);
}
}
void loginUser(String email, String password) async {
try {
if (email.isNotEmpty && password.isNotEmpty) {
await firebaseAuth.signInWithEmailAndPassword(
email: email, password: password);
print('log success');
} else {
Get.snackbar(
'Error Logging in',
'Please enter all the fields',
);
}
} catch (e) {
Get.snackbar(
'Error Logging in',
e.toString(),
);
}
}
updateUserData(Map<String, dynamic> data) {
print("UPDATED");
firestore.collection('users').doc(_user.value?.uid).update(data);
}
Stream<model.User> listenToUser() => firestore
.collection('users')
.doc(_user.value?.uid)
.snapshots()
.map((snapshot) => model.User.fromSnap(snapshot));
}
User Model code:
class User {
// static const UID = "uid";
// static const NAME = "name";
// static const EMAIL = "email";
String uid;
String name;
String email;
List<CartItemModel> cart;
User(
{required this.name,
required this.email,
required this.uid,
required this.cart});
Map<String, dynamic> toJson() =>
{"name": name, "email": email, "uid": uid, "cart": cart};
// static User fromSnap(DocumentSnapshot snap) {
static User fromSnap(DocumentSnapshot snap) {
var snapshot = snap.data() as Map<String, dynamic>;
return User(
name: snapshot['name'],
email: snapshot['email'],
uid: snapshot['uid'],
cart: _convertCartItems(snapshot['cart'] ?? []));
}
// List<CartItemModel> _convertCartItems(List cartFromDb) {
static List<CartItemModel> _convertCartItems(List cartFromDb) {
List<CartItemModel> _result = [];
// logger.i(cartFromDb.lengt);
print(cartFromDb.length);
cartFromDb.forEach((element) {
_result.add(CartItemModel.fromMap(element));
});
return _result;
}
}
Also I referred this github for shopping cart functionality but I have made some changes to make it null safe: cart functionality github
Use Rxn<T>() for nullable rx:
final _user = Rxn<User>();
Then on onInit():
_user.bindStream(firebaseAuth.authStateChanges());
I have a custom 'User' class and I am trying to retrieve user's data from firestore based on its uid. I am successfully able to map it into my custom 'User' object however I cant return in from that function. Here is the code snippet:
class Wrapper extends StatelessWidget {
// set activeUser globally
User getCurrentUserObject(String uid) {
User _user;
var doc = DatabaseService().userCollection.document(uid);
doc.get().then((doc) {
_user = User().getUserFromData(doc.data);
print(" user: $_user");
});
return _user;
}
#override
Widget build(BuildContext context) {
ScreenUtil.init(context);
final currentUser = Provider.of<FirebaseUser>(context);
if (currentUser == null) {
print("No user logged in");
return Auth();
} else {
print("USER: ${getCurrentUserObject(currentUser.uid).email}");
print("Current user: ${currentUser.uid}");
return CustomerHomeScreen();
}
}
}
Here is the error snippet:
════════ Exception caught by widgets library ═══════════════════════════════════
The getter 'email' was called on null.
Receiver: null
Tried calling: email
The relevant error-causing widget was
Wrapper
lib/main.dart:20
════════════════════════════════════════════════════════════════════════════════
I/flutter (30822): user: Instance of 'User'
I/flutter (30822): user: Instance of 'User'
So far I have figured that it is not able return the User instance. How can i achieve that?
Thank you :)
UPDATE: Adding User class which contains getUserFromData() method.
class User {
final String uid;
final String name;
final String email;
final String phone;
final int gender;
final int type;
User({this.uid, this.name, this.email, this.phone, this.gender, this.type});
User getUserFromData(Map<String, dynamic> data) {
return User(
uid: data['uid'] ?? '',
name: data['name'] ?? '',
email: data['email'] ?? '',
phone: data['phone'] ?? '',
gender: data['gender'] ?? -1,
type: data['type'] ?? -1);
}
Map<String, dynamic> toJson() {
return {
'uid': uid,
'name': name,
'email': email,
'phone': phone,
'gender': gender,
'type': type
};
}
}
You're getting the error because your getCurrentUserObject method returns the _user variable before doc.get() completes. You can fix this by changing your code from using .then to using await, so you wait for the result of doc.get() before executing to the next line.
So, this:
User getCurrentUserObject(String uid) {
//Rest of the code
doc.get().then((doc) {
_user = User().getUserFromData(doc.data);
print(" user: $_user");
});
return _user;
}
becomes:
Future<User> getCurrentUserObject(String uid) async {
//Rest of the code
var doc = await doc.get();
_user = User().getUserFromData(doc.data);
print(" user: $_user");
return _user;
}
DatabaseService Class
class DatabaseService {
final String uid;
DatabaseService({this.uid});
//collection reference
final CollectionReference agentsCollection =
Firestore.instance.collection('agents');
Future updateUserData(
String fullname,
String phonenumber,
String email,
String profession,
String city
)
async {
return await agentsCollection.document(uid).setData({
'fullname': fullname,
'phonenumber': phonenumber,
'email': email,
'profession': profession,
'city': city,
});
}
AuthService Class
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
//create user object based on FirebaseUser
User _userFromFirebaseUser(FirebaseUser user) {
return user != null ? User(uid: user.uid) : null;
}
//auth change user stream
Stream<User> get user {
return _auth.onAuthStateChanged
.map(_userFromFirebaseUser);
}
Then I have a function to Register the user in this class as below:
//Register with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try{
AuthResult result = await _auth.createUserWithEmailAndPassword
(email: email, password: password);
FirebaseUser user = result.user;
//create a new document for the user with the uid
await DatabaseService(uid: user.uid).updateUserData( 'hardcoded Name',
'Hardcoded Number','hardcoded email', 'hardcoded profession', 'hardcoded city');
return _userFromFirebaseUser(user);
} catch(e){
print(e.toString());
return null;
}
}
SignUp Class
In this class I call the user (uid) using Provider service. Then I use the function from the AuthService Class which uses the function from the DatabaseService class in order to create a new document in the Database.
RaisedButton(
elevation: 5.0,
padding: EdgeInsets.only(left: 60, right: 60 , top: 15, bottom:15),
color: Colors.blue,
child: Text(
'Sign Up',
style: TextStyle(color: Colors.white),
),
onPressed: () async {
if(_formKey.currentState.validate()){
setState(() => loading= true);
dynamic result = await _auth.registerWithEmailAndPassword(email, password);
await DatabaseService (uid: user.uid).updateUserData(this.fullname,
this.phonenumber, this.email, this.profession, this.city);
if(result == null) {
setState(() {
error = 'please supply a valid email';
loading = false;
});
} else {
Navigator.push(context, new MaterialPageRoute(
builder: (context) => SignIn()
));
}
}
}
),
So Given all this, for some reason it's only storing the hard coded data from the AuthService class and not performing the line in the SignUp class under the onPressed() function; where I am telling it to use the function updateUserData and store the this.email, this.fullname, etc. Also an important note, in the SignUp class it says returned null user (assuming uid).
Please someone help me if you get what I am doing wrong in this context.
I think when parsing the data the hard coded data remains. here is an example of how to fix it.
create a user obj
class User {
final String userID;
final String displayName;
final String email, pushToken;
final String phoneNumber;
final String profilePictureURL;
User({
this.phoneNumber,
this.userID,
this.pushToken,
this.displayName,
this.email,
this.profilePictureURL,
});
Map<String, Object> toJson() {
return {
'pushToken': pushToken,
'phoneNumber': phoneNumber,
'userID': userID,
'displayName': displayName,
'email': email == null ? '' : email,
'profilePictureURL': profilePictureURL,
'appIdentifier': 'my app'
};
}
factory User.fromJson(Map<String, Object> doc) {
User user = new User(
pushToken: doc['pushToken'],
userID: doc['userID'],
displayName: doc['displayName'],
phoneNumber: doc['phoneNumber'],
email: doc['email'],
profilePictureURL: doc['profilePictureURL'],
);
return user;
}
factory User.fromDocument(DocumentSnapshot doc) {
return User.fromJson(doc.data);
}
}
and then the sign up method
//user sign up
static Future<String> signUp(String email, String password) async {
final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
FirebaseUser user = (await _firebaseAuth.createUserWithEmailAndPassword(
email: email, password: password))
.user;
return user.uid;
}
//if user id doesnt exist add user
static void addUser(User user) async {
checkUserExist(user.userID).then((value) {
if (!value) {
print("user ${user.displayName} ${user.email} added");
Firestore.instance
.document("users/${user.userID}")
.setData(user.toJson());
Fluttertoast.showToast(
msg: "user ${user.displayName} ${user.email} added");
} else {
print("user ${user.displayName} ${user.email} exists");
}
});
}
//check if user exists
static Future<bool> checkUserExist(String userID) async {
bool exists = false;
try {
await Firestore.instance.document("users/$userID").get().then((doc) {
if (doc.exists)
exists = true;
else
exists = false;
});
return exists;
} catch (e) {
return false;
}
}
and to handle your button onPressed: and it will save your data to firestore
await signUp(email, password).then((uID) {
addUser(new User(
userID: uID,
email: email,
displayName: fullname,
phoneNumber: number,
pushToken: pushToken,
profilePictureURL: ''));
Hey #Ggriffo i used this code in the onPressed function:
dynamic result = await _auth.registerWithEmailAndPassword
(email, password).then((user) {
DatabaseService (uid: user.uid).updateUserData(this.fullname,
this.phonenumber, this.email, this.profession, this.city);
});
and now it works! i used this logic from your onPressed piece of code. Thanks!