How to get user id from firebase auth as string? - firebase

I'm trying to insert data to firestore with the authenticated user id as the document id but i got null from the parameter.
Please take a look at my script.
void didChangeDependencies() {
uid = '';
super.didChangeDependencies();
}
Future _fetchUID() async {
var auth = AuthProvider.of(context).auth;
return await auth.getCurrentUser().then((user) => _uid = user.uid);
}
_validateAndSubmit() async {
setState(() {
_errorMessage = '';
_isLoading = true;
});
if (_validateAndSave()) {
try {
_fetchUID();
await Report.create(_uid, _suspectName, _plateNumber, _civilizationId,
_drivingLicenseId, _clause, _description);
return Navigator.of(context).pop();
} catch (e) {
setState(() {
_isLoading = false;
_errorMessage = e.message;
});
print(_errorMessage);
throw Exception(e);
}
}
}
In this method below you can see that I have already tried to set the _uid, but I still cannot get the _uid value.
Future _fetchUID() async {
var auth = AuthProvider.of(context).auth;
return await auth.getCurrentUser().then((user) => _uid = user.uid);
}
This is how the getCurrentUser() method looks like.
Future<FirebaseUser> getCurrentUser() async {
FirebaseUser user = await _firebaseAuth.currentUser();
return user;
}
Am I doing it wrong?

First of all, you mixed the Future.then syntax and the async await syntax.
You should probably write your method this way:
void _fetchUID() async {
var auth = AuthProvider.of(context).auth;
_uid = (await auth.getCurrentUser()).uid;
}
If _uid is still null for you after calling _fetchUID this way, then you are simply not signed in, meaning that there is no FirebaseUser as you first need to sign in.
In your _validateAndSubmit method, you also first need to await your _fetchUID call, otherwise it will pass _uid before it has been assigned a value.
...
try {
await _fetchUID();
await Report.create(_uid, ..);
...

Related

Is there a way to use the results of a catch block inside a widget in Flutter

I am building a flutter app with Firebase as the back end.
I have created an AuthService class on a separate file and import and use the Auth functions inside the log in screen.
This is my AuthService Class.
class AuthService {
Future<UserModel?> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return _userFromFirebase(cred.user);
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return null;
}
}
}
In the sign in page, I initialize the function:
final auth = Provider.of<AuthService>(context);
Then use it in an onPressed :
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
dynamic result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result == null) {
setState(() {
errorSigningIn = 'Sign in error';
//this is where I want to use the error response.
});
}
},
I am stuck on using the error I catch in the signInWithEmailAndPassword function and assigning it to the errorSigningIn variable in the SignIn widget.
I am new to this, please help.
Thanks.
You can create your own class to handle auth results. For example:
class AuthResult {
final int code;
final UserModel? user;
final String? errorMessage;
AuthResult(this.code, {
this.user,
this.errorMessage,
});
}
This class can help you to handle all cases of logging in. And this is what you should do with your signing in method:
class AuthService {
Future<AuthResult> signInWithEmailAndPassword(
String email, String password) async {
try {
final cred = await _auth.signInWithEmailAndPassword(
email: email, password: password);
return AuthResult(200, user: _userFromFirebase(cred.user));
} on auth.FirebaseAuthException catch (e) {
print(e.toString());
return AuthResult(0 /*<-- your error result code*/, e.toString());
}
}
}
And, finally, your onPressed:
press: () async {
// SIGN IN WITH EMAIL AND PASSWORD
AuthResult result =
await auth.signInWithEmailAndPassword(
email, password);
// IF SIGN IN FAILS
if (result.code != 200) {
setState(() {
errorSigningIn = result.errorMessage; //<-- Get your error message
//this is where I want to use the error response.
});
}
},

A document path must be a non-empty string, Flutter - Firebase error?

I have some mistakes with flutter and firebase, if someone can help would be great here is my auth controller
class AuthController extends GetxController {
final FirebaseAuth auth = FirebaseAuth.instance;
final Rxn<User> _firebaseUser = Rxn<User>();
Rx<XFile>? _pickedImage;
XFile? get profilePhoto => _pickedImage?.value;
// final user = FirebaseAuth.instance.currentUser.obs;
Rxn<User> get user => _firebaseUser;
// final user = FirebaseAuth.instance.currentUser;
#override
onInit() {
_firebaseUser.bindStream(auth.authStateChanges());
super.onInit();
}
// void register(
// String name, String email, String password, XFile? image) async {
// try {
// UserCredential _authResult = await auth.createUserWithEmailAndPassword(
// email: email.trim(), password: password);
// //create user in database.dart
// String downloadUrl = await uploadToStorage(image!);
// UserModel _user = UserModel(
// id: _authResult.user?.uid,
// name: name,
// email: _authResult.user?.email,
// profilePic: downloadUrl,
// );
// if (await Database().createNewUser(_user)) {
// Get.find<UserController>().user = _user;
// }
// } catch (e) {
// Get.snackbar(
// "Error creating Account",
// e.toString(),
// snackPosition: SnackPosition.BOTTOM,
// );
// }
// }
void register(
String name, String email, String password, XFile? image) async {
try {
if (name.isNotEmpty &&
email.isNotEmpty &&
password.isNotEmpty &&
image != null) {
// save out user to our ath and firebase firestore
UserCredential _authResult = await auth.createUserWithEmailAndPassword(
email: email,
password: password,
);
String downloadUrl = await uploadToStorage(image);
UserModel _user = UserModel(
id: _authResult.user?.uid,
name: name,
email: _authResult.user?.email,
profilePic: downloadUrl,
);
if (await Database().createNewUser(_user)) {
Get.find<UserController>().user = _user;
} else {
Get.snackbar(
'Error Creating Account',
'Please enter all the fields',
);
}
}
} catch (e) {
Get.snackbar(
'Error Creating Account',
e.toString(),
);
}
}
void login(String email, password) async {
try {
UserCredential _authResult = await auth.signInWithEmailAndPassword(
email: email.trim(), password: password);
Get.find<UserController>().user =
await Database().getUser(_authResult.user?.uid ?? '');
} catch (e) {
Get.snackbar("About User", "User message",
snackPosition: SnackPosition.BOTTOM,
titleText: Text("Acount creation failed"),
messageText:
Text(e.toString(), style: TextStyle(color: Colors.white)));
}
}
Future<void> signOut() async {
await auth.signOut();
Get.find<UserController>().clear();
}
Future pickImage() async {
print("call on click add photo icon");
final ImagePicker _picker = ImagePicker();
final XFile? pickedImage =
await _picker.pickImage(source: ImageSource.gallery);
print('picked image filled with image from gallery'); //This doesnt print at
if (pickedImage != null) {
Get.snackbar('Profile Picture',
'You have successfully selected your profile picture!');
// print(pickedImage.path);
}
_pickedImage = Rx<XFile>(pickedImage!);
// print(_pickedImage);
// print(profilePhoto);
}
// upload to firebase storage
Future<String> uploadToStorage(XFile? image) async {
Reference ref = FirebaseStorage.instance
.ref('')
.child('profilePics')
.child(auth.currentUser!.uid);
// print(ref);
UploadTask uploadTask = ref.putFile(File(image?.path ?? 'idemo'));
print(uploadTask);
// TaskSnapshot snap = await uploadTask;
String downloadUrl = await (await uploadTask).ref.getDownloadURL();
print(downloadUrl);
return downloadUrl;
}
}
Here is my function to createNewUser
class Database {
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future<bool> createNewUser(UserModel user) async {
try {
await _firestore.collection("users").doc(user.id).set({
"name": user.name,
"email": user.email,
"profilePhoto": user.profilePic
});
return true;
} catch (e) {
print(e);
return false;
}
}
Here is HomeController
class HomeController extends GetxController {
final Rxn<List<TodoModel>> todoList = Rxn<List<TodoModel>>([]);
var selectedDate = DateTime.now().obs;
List<TodoModel>? get todos => todoList.value;
#override
void onInit() {
super.onInit();
String? uid = Get.find<AuthController>().auth.currentUser?.uid ?? '';
print(uid);
todoList.bindStream(Database().todoStream(uid));
}
chooseDate() async {
DateTime? pickedDate = await showDatePicker(
context: Get.context!,
initialDate: selectedDate.value,
firstDate: DateTime(2000),
lastDate: DateTime(2024),
//initialEntryMode: DatePickerEntryMode.input,
// initialDatePickerMode: DatePickerMode.year,
);
if (pickedDate != null && pickedDate != selectedDate.value) {
selectedDate.value = pickedDate;
}
}
}
and here is View page
GetX<HomeController>(
init: Get.put<HomeController>(HomeController()),
builder: (HomeController todoController) {
if (todoController.todos != null) {
// print(todoController.todos?.done ?? false);
return Expanded(
child: ListView.builder(
itemCount: todoController.todos?.length,
itemBuilder: (_, index) {
return TodoCard(
uid: controller.user.value?.uid ?? '',
todo: todoController.todos![index],
);
},
),
);
} else {
return Text("loading...");
}
},
),
So, I have an error when I register a new user I got this error:
The following assertion was thrown building Builder(dirty):
a document path must be a non-empty string
Failed assertion: line 116 pos 14: ‘path.isNotEmpty’
And here is output from terminal:
The relevant error-causing widget was
GetMaterialApp
lib/main.dart:23
When the exception was thrown, this was the stack
#2 _JsonCollectionReference.doc
#3 Database.todoStream
#4 HomeController.onInit
#5 GetLifeCycleBase._onStart
#6 InternalFinalCallback.call
#7 GetInstance._startController
#8 GetInstance._initDependencies
#9 GetInstance.find
#10 GetInstance.put
#11 Inst.put
So a problem is with this path, and when I reload from the visual studio I god the right user with the right data. So the problem is when I register a user for the first time.
It looks like uid is empty, which you should also be able to see from looking up print(uid); in your output.
When your application or web page loads, Firebase automatically tries to restore the previously signed in user from its local state. This requires that it makes a call to the server however (for example to check if the account has been disabled) and while that call is going on, your main code continues to execute and the currentUser variable is going to be null.
Your code needs to take this into account. The easiest way to do this is to not depend on currentUser, but instead to use an reactively respond to changes in the authentication state as shown in the first example in the documentation on getting the current user:
FirebaseAuth.instance
.authStateChanges()
.listen((User? user) {
if (user != null) {
print(user.uid);
}
});
The authStateChange method here returns a stream that fires an event whenever the authentication state changes, so when the user signs in or signs out. The common way to use this stream is to either set the user to the state of your widget, or to use the stream directly in a StreamBuilder.

The getter 'uid' not defined

i'm trying to create a food track app on android studio, it's my first time and i'm working with firebase_auth 3.3.12. my code in the aut.dart is:
import 'package:firebase_auth/firebase_auth.dart';
import 'package:my_firstapp/models/user_model.dart';
class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;
AuthService();
// create user object based on FirebaseUser.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
// auth change user stream
Stream<UserModel> get user {
return _auth.authStateChanges()
.map(_userFromUser);
}
Future<UserModel> getUser() async {
User user = await _auth.currentUser();
return _userFromUser(user);
}
// sign in with email and password
Future signInWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.signInWithEmailAndPassword(email: email, password: password);
User user = result.user;
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign up with email and password
Future registerWithEmailAndPassword(String email, String password) async {
try {
UserCredential result = await _auth.createUserWithEmailAndPassword(email: email, password: password);
User user = result.user;
// create a new user document in database
return _userFromUser(user);
} catch(e) {
print(e.toString());
return null;
}
}
// sign out
Future signOut() async {
try {
return await _auth.signOut();
} catch(e){
print(e.toString());
return null;
}
}
}
However i'm getting 2 errors:
-The getter 'uid' isn't defined for the type 'Stream';
-The expression "await _auth.currentUser()" doesn't evaluate to a function, so it can't be invoked.
How can i rewrite the code? thanks
The _auth.currentUser is not a function (it used to be, but changed about a year ago), but rather a property. It also isn't asynchronous, so you don't need await nor to return a Future.
So:
UserModel getUser() {
User user = _auth.currentUser;
return _userFromUser(user);
}
In this code, your argument is capitalised ('User') but in the code block you write 'user'.
UserModel _userFromUser(User) {
return user != null ? UserModel(uid: user.uid) : null;
}
Furthermore, for _auth.currentUser(), you do not need to use await as it does not return a future.

DateTime not a subtype of type TimeStamp/Unhandled Exception: Invalid argument: Instance of 'Future<LocationData>

So I am using the nearby connections API to discover devices around me and store their data in firestore however I keep getting 2 warnings about the location I am getting from the user that I came in contact with and the time i came in contact with them
These are the 2 warnings:
1)DateTime not a subtype of type TimeStamp
2)Unhandled Exception: Invalid argument: Instance of Future<.LocationData.>
as I try to add these values to firestore
here is my discovery method:
void discovery() async {
try {
bool a = await Nearby().startDiscovery(loggedInUser.email, strategy,
onEndpointFound: (id, name, serviceId) async {
print('I saw id:$id with name:$name'); // the name here is an email
var docRef =
_firestore.collection('users').document(loggedInUser.email);
// When I discover someone I will see their email
docRef.collection('met_with').document(name).setData({
'email': await getUsernameOfEmail(email: name),
'contact time': DateTime.now() as Timestamp ,
'contact location': location.getLocation(),
});
}, onEndpointLost: (id) {
print(id);
});
print('DISCOVERING: ${a.toString()}');
} catch (e) {
print(e);
}
}
This is another method where I retrieve the info I discovered from firestore:
void addContactsToList() async {
await getCurrentUser();
_firestore
.collection('users')
.document(loggedInUser.email)
.collection('met_with')
.snapshots()
.listen((snapshot) {
for (var doc in snapshot.documents) {
String currEmail = doc.data['email'];
DateTime currTime = doc.data.containsKey('contact time')
? (doc.data['contact time'] as Timestamp).toDate()
: null;
String currLocation = doc.data.containsKey('contact location')
? doc.data['contact location']
: null;
String _infection = doc.data['infected'];
if (!contactTraces.contains(currEmail)) {
contactTraces.add(currEmail);
contactTimes.add(currTime);
contactLocations.add(currLocation);
infection.add(_infection);
}
}
setState(() {});
print(loggedInUser.email);
});
}
Any fix for this please?
Use an async function to convert the Future<.LocationData.> to LocationData.
var data;
void convertData() async{
var futuredata = await FutureLocationData;
setState(() {
data = futuredata });
}

Flutter/Firebase : How can i access the current user without using '.then(...)' function

I'm trying to avoid using the .then((u) { return u.uid }) function in all my code where I need to access the current user's UID, instead just by calling getCurrentUser().uid for a much faster access. However, it gives me an error The getter 'uid' was called on null. but it's not null because it does print in the console but only after showing that it's null and the error at the end for some reason. I'm not well knowledge in the Future/Async/Await logic so any help would be greatly appreciated!
class UsersAPI {
final DatabaseReference usersRef = FirebaseDatabase.instance.reference().child(Config.users);
Future<FirebaseUser> currentUser() async {
return await FirebaseAuth.instance.currentUser();
}
FirebaseUser getCurrentUser() {
FirebaseUser user;
this.currentUser().then((u) {
user = u;
print('USER 1 $user'); // Prints after 'USER 2'
});
print('USER 2 $user'); // Prints first
if (user != null) {
return user;
} else {
return null;
}
}
DatabaseReference getCurrentUserRef() {
return this.usersRef.child(this.getCurrentUser().uid); // GIVES THE 'uid' WAS CALLED ON NULL ERROR
}
observeCurrentUser(Function onSuccess(User u)) {
this.usersRef.child(this.getCurrentUser().uid).onValue.listen( (event) { // GIVES THE 'uid' WAS CALLED ON NULL ERROR
DataSnapshot snapshot = event.snapshot;
if (snapshot.value != null) {
User user = User().transform(snapshot.key, snapshot.value);
onSuccess(user);
}
});
}
observeUser(String userID, Function onSuccess(User u), Function onFailure(String e)) {
this.usersRef.child(userID).onValue.listen( (e) {
DataSnapshot snapshot = e.snapshot;
if (snapshot.value != null) {
User user = User().transform(snapshot.key, snapshot.value);
onSuccess(user);
} else {
onFailure("User Not Found...");
}
});
}
}
Example Usage - WORKS:
APIs().usersAPI.currentUser().then((u) {
APIs().usersAPI.observeUser(u.uid, (u) {
onSuccess(u);
}, (e) {
print(e);
});
});
DOESN'T WORK:
APIs().usersAPI.observeCurrentUser((u) {
onSuccess(u);
});
DatabaseReference getCurrentUserRef() async {
return this.usersRef.child((await this.getCurrentUser()).uid); =
}
than call
var ref = await getCurrentUserRef()
Little bit more pretty
DatabaseReference getCurrentUserRef() async {
var firebaseUser = await this.getCurrentUser();
return this.usersRef.child(firebaseUser.uid);
}
EDIT: to clarify some question on asynchronicity
How would you call now this function to get the reference?
Lets say you want to update the data on the user, you can do
Firestore.instance.runTransaction((transaction) async {
var reference = await getCurrentUserRef();
await transaction.set(reference, someData);
});
Or you would like to read the data from that reference
readAndProcessData() async {
var reference = await getCurrentUserRef();
DocumentSnapshot user = await reference.get();
print(user.data.toString);
}

Resources