I'm pretty new to flutter firebase and I am getting this error NoSuchMethodError: The getter 'uid' was called on null flutter. I have searched and looked very carefully but left scratching my head only where is the error. Over on my condition I'm checking if user is not equal to null then save it but its not happening.
Here's my phone authentication file code which should save the user as well but its not doing it right now because of the null issue
class PhoneAuthService {
FirebaseAuth auth = FirebaseAuth.instance;
User user = FirebaseAuth.instance.currentUser;
CollectionReference users = FirebaseFirestore.instance.collection('users');
Future<void> addUser(context, uid) async {
final QuerySnapshot result = await users.where('uid', isEqualTo: uid).get();
List<DocumentSnapshot> document = result.docs;
if (document.length > 0) {
Navigator.pushReplacementNamed(context, LocationScreen.id);
} else {
return users.doc(user.uid).set({
'uid': user.uid,
'mobile': user.phoneNumber,
'email': user.email,
}).then((value) {
Navigator.pushReplacementNamed(context, LocationScreen.id);
}).catchError((error) => print("Failed to add user: $error"));
}
}
Future<void> verifyPhonenumber(BuildContext context, number) async {
final PhoneVerificationCompleted verificationCompleted =
(PhoneAuthCredential credential) async {
await auth.signInWithCredential(credential);
};
final PhoneVerificationFailed verificationFailed =
(FirebaseAuthException e) {
if (e.code == 'invalid-phone-number') {
print('The phone number is not valid');
}
print("The error is ${e.code}");
};
final PhoneCodeSent codeSent = (String verId, int resendToken) async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => OTPScreen(
number: number,
verId: verId,
)));
};
try {
auth.verifyPhoneNumber(
phoneNumber: number,
verificationCompleted: verificationCompleted,
verificationFailed: verificationFailed,
codeSent: codeSent,
timeout: const Duration(seconds: 60),
codeAutoRetrievalTimeout: (String verificationId) {
print(verificationId);
});
} catch (e) {
print("Error ${e.toString()}");
}
}
}
This is where I'm getting the user otp and proceeding it further
Future<void> phoneCredential(BuildContext context, String otp) async {
FirebaseAuth _auth = FirebaseAuth.instance;
try {
PhoneAuthCredential credential = PhoneAuthProvider.credential(
verificationId: widget.verId, smsCode: otp);
final User user = (await _auth.signInWithCredential(credential)).user;
if (user != null) {
_services.addUser(context, user.uid);
Navigator.pushReplacementNamed(context, LocationScreen.id);
} else {
print("Login Process Failed ");
if (mounted) {
setState(() {
error = 'Login Failed';
});
}
}
} catch (e) {
print(e.toString());
if (mounted) {
setState(() {
error = 'Invalid OTP';
});
}
}
}
If anyone understood where I'm doing wrong please help I'm stuck for 3 days on it now.
Related
I am following this guide to build my App. Then, I add a document listener to the "users" collection. Now the problem is even if I log out of the first account and log in to the second account, the stream is still reading the previous document if I manually change the document using the console.
Account _userInfo = Account(email: '', name: '', typeName: '', fcmToken: '', isDisable: false);
Account get userInfo => _userInfo;
StreamSubscription<DocumentSnapshot>? userListener;
Future<void> init() async {
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseAuth.instance.userChanges().listen((user) async {
if (user != null) {
print('User != null');
_loginState = ApplicationLoginState.loggedIn;
await setFcmToken();
listenToMyself();
} else {
print('User null');
userListener?.cancel();
_loginState = ApplicationLoginState.loggedOut;
}
notifyListeners();
});
void listenToMyself() {
print('Listening to: ${FirebaseAuth.instance.currentUser!.uid}');
userListener = usersCollection.doc(FirebaseAuth.instance.currentUser!.uid).snapshots().listen(
(event) {
print("current data: ${FirebaseAuth.instance.currentUser!.uid} ${event.data()}");
_userInfo = Account.fromJson(event.data() as Map<String, dynamic>);
notifyListeners();
},
onError: (error) => print("Listen failed: $error"),
);
}
Future<void> setFcmToken() async {
String? token = await FirebaseMessaging.instance.getToken();
_token = token;
await FirebaseFirestore.instance.collection('users').doc(FirebaseAuth.instance.currentUser!.uid).set({'fcmToken': token}, SetOptions(merge: true));
}
I want to store the URL of the image like this -
this is how i want to store url of the image in firestore
This is my current condition -
Currently it's showing the path of the image not the url
onPressed: () async {
if (imageFile == null) {
print("error");
} else {
try {
final ref = storage
.ref()
.child('user_profile_images')
.child(name + '.jpg');
ref.putFile(imageFile!);
url = await ref.getDownloadURL();
if (url != null) {
print(url);
}
} catch (e) {
print(e);
}
dynamic user = await auth.createUserWithEmailAndPassword(
email: email, password: password);
await FirebaseFirestore.instance
.collection('users')
.doc(name)
.set({
'email': email,
'name': name,
'profileurl': imageFile?.path,
'userid': auth.currentUser?.uid
}).then((value) {
try {
if (user != null) {
Navigator.pushNamed(context, '/dashboard');
}
} catch (e) {
print(e);
}
});
}
}),
I have a feeling the multiple try/catch blocks are getting in your way. In addition, it is never a good idea to mix then and await in a single block of code.
Combining those, try the following:
try {
final ref = storage
.ref()
.child('user_profile_images')
.child(name + '.jpg');
ref.putFile(imageFile!);
url = await ref.getDownloadURL();
if (url != null) {
print(url);
}
dynamic user = await auth.createUserWithEmailAndPassword(
email: email, password: password);
dynamic value = await FirebaseFirestore.instance
.collection('users')
.doc(name)
.set({
'email': email,
'name': name,
'profileurl': url // 👈
'userid': auth.currentUser?.uid
});
if (user != null) {
Navigator.pushNamed(context, '/dashboard');
}
} catch (e) {
print(e);
}
'profileurl': imageFile?.path, => 'profileurl': url?.toString(),
I'm currently facing this error message at login, my registration function works fine. but can't seem to figure out why my login keeps bringing this error. I would appreciate if I can be pointed to where my mistake is coming from.
void loginUser() async {
User user;
await _auth
.signInWithEmailAndPassword(email: email.text, password: password.text)
.then((value) {
setState(() {
loading = false;
});
return value;
}).catchError((error) {
Navigator.pop(context);
Get.defaultDialog(title: 'Error in Login');
});
if (user != null) {
readData(user).then((value) => Get.toNamed(homeRoute));
}
}
Future readData(User user) async {
FirebaseFirestore.instance
.collection('users')
.doc(user.uid)
.get()
.then((result) async {
await App.sharedPreferences.setString('uid', result.get('uid'));
await App.sharedPreferences.setString('email', result.get('email'));
await App.sharedPreferences.setString('fullname', result.get('fullname'));
await App.sharedPreferences.setString('bvn', result.get('bvn'));
});
}
Here's my Registration function
void _registerUser() async {
User user;
await _auth
.createUserWithEmailAndPassword(
email: email.text, password: password.text)
.then((value) {
user = value.user;
}).catchError((error) {
Navigator.pop(context);
Get.defaultDialog(title: 'Error in registration');
});
if (user != null) {
saveToFirestore(user).then((value) => Get.toNamed(homeRoute));
}
}
Future saveToFirestore(User user) async {
FirebaseFirestore.instance.collection('users').doc(user.uid).set({
'uid': user.uid,
'fullname': fullname.text.trim(),
'email': user.email,
'bvn': bvn.text
});
await App.sharedPreferences.setString('uid', user.uid);
await App.sharedPreferences.setString('email', user.email);
await App.sharedPreferences.setString('fullname', fullname.text);
await App.sharedPreferences.setString('bvn', bvn.text);
}
Change
User user;
await _auth
to User user = await _auth....
And in your FireStore query, use
Future readData(User user) async {
FirebaseFirestore.instance
.collection('users')
.doc(user.user.uid) // instead of user.uid
.get()
Everywhere with user.uid change it to user.user.uid.
Hey I am trying to use Firebase Auth verifyPhoneNumber in Flutter.
I am constantly getting my msg in onTap function as null. I tried declaring a variable in registerToFb function but I am not able to update its value. How do I solve it?
Following is the function from where I am calling the firebaseAuth.verifyPhoneNymber() method.
onTap: () async {
setState(() => isLoading = true);
String msg = await _auth.registerToFb(
"+91$truephoneNumber", context);
print(msg);
},
Following is where I am defining my function.
class AuthenticationService {
final FirebaseAuth _firebaseAuth;
AuthenticationService(this._firebaseAuth);
Future registerToFb(String phoneNumber, BuildContext context) async {
String result = " ";
await _firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 30),
verificationCompleted: (PhoneAuthCredential credential) async {
UserCredential authresult =
await _firebaseAuth.signInWithCredential(credential);
User user = authresult.user;
_getUserFromFirebase(user);
result = "signedUp";
},
verificationFailed: (FirebaseAuthException e) {
String error = e.code == 'invalid-phone-number'
? "Invalid number. Enter again."
: "Can Not Login Now. Please try again.";
result = error;
},
codeSent: (String verificationId, int resendToken) {
result = "verified";
},
codeAutoRetrievalTimeout: (String verificationId) {},
);
}
return result;
}
Help me, please. Thanks
The problem is that you aren't returning anything in your function. And even when you try to save a value from inside the callbacks, the future from verifyPhoneNumber resolves before those callbacks get called.
You need to be awaiting the right thing. You shouldn't await the future returned from verifyPhoneNumber. You should await a new future that doesn't resolve until you get a result from one of the callbacks. This means you need to create your own Future which you can manually resolve specifically when you need it to. You can do this with a Completer:
Future<String> registerToFb(String phoneNumber, BuildContext context) {
final completer = Completer<String>();
_firebaseAuth.verifyPhoneNumber(
phoneNumber: phoneNumber,
timeout: const Duration(seconds: 30),
verificationCompleted: (PhoneAuthCredential credential) async {
UserCredential authresult =
await _firebaseAuth.signInWithCredential(credential);
User user = authresult.user;
_getUserFromFirebase(user);
completer.complete("signedUp");
},
verificationFailed: (FirebaseAuthException e) {
String error = e.code == 'invalid-phone-number'
? "Invalid number. Enter again."
: "Can Not Login Now. Please try again.";
completer.complete(error);
},
codeSent: (String verificationId, int resendToken) {
completer.complete("verified");
},
codeAutoRetrievalTimeout: (String verificationId) {
completer.complete("timeout");
},
);
return completer.future;
}
In the moment of registering, when I try to print the username which is a parameter of the function, it just won't print anything on the screen and I cannot understand why.
Future registerWithEmailAndPassword(String username, 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
print(username);
await DatabaseService(uid: user.uid).updateUserData(username);
return _userFromFirebaseUser(user);
} catch (error) {
print(error.toString());
return null;
}
}
Edit: this is the function which calls the registerWithEmailAndPassword function:
onPressed: () async {
if (error.length > 0)
error = "";
if (_formKey.currentState.validate()) {
setState(() => loading = true);
dynamic result = await _auth.registerWithEmailAndPassword(username, email, password);
if (result == null){
if (this.mounted){
setState(() {
error = 'Please supply a valid email';
loading = false;
});}
}
}
}