flutter await result always null - firebase

This is my function
Future<Address> readAddress() async {
database = FirebaseDatabase(app: app);
await database
.reference()
.child(table_name)
.child(uid)
.once()
.then((DataSnapshot snapshot) {
print("address_start");
if (snapshot.value == null) {
print("address_start_value_null");
return null;
} else {
print("address_start_value_not_null");
print(snapshot.value);
Address a = Address().map(snapshot.value);
return a;
// return a;
}
}).catchError((onError) {
print(onError);
return onError;
});
}
This is my function call
readAddress().then((address) {
if (address != null) {
print("address read seucssfully " + address.firstname);
} else {
print(
"address read faield result is null $address"); // + address.toString());
}
}).catchError((onError) {
print("error on read address");
});
But here always it returns null.
What is wrong here?
message from readAddress() function
[dart] This function has a return type of 'Future', but
doesn't end with a return statement. [missing_return]
I don't know to explain more StackOverflow showing this error message when in try to post this question "t looks like your post is mostly code; please add some more details.
"

The problem with your function is that it's not returning a Future, but the Address object instead. I would rewrite your function like this to just return the Address object
Future<Address> readAddress() async {
try{
database = FirebaseDatabase(app: app);
DataSnapshot snapshot = await database
.reference()
.child(table_name)
.child(uid)
.once();
return Address().map(snapshot.value);
}catch(e) {
print(e);
return(e);
}
}
With this, your function call can be just this:
Address address = readAddress();
Simple, isn't it? I have taken care all of the error handling inside the function.

Related

Why is it the passed data returns null in Flutter? I use Cloud Firestore as my data storage

I have a file named todo_repository.dart and has readTodo() function. Whenever I print the data variable here, there are returned contents/values but after I passed the data to another file named todo_list_cubit.dart, the data returns null value.
Here is the readTodo() of todo_repository.dart
Future<dynamic> readTodo() async {
try {
final User user = auth.currentUser!;
final uid = user.uid;
await usersTodo.doc(uid).get().then((DocumentSnapshot documentSnapshot) {
if (documentSnapshot.exists) {
var data = documentSnapshot.data() as Map;
//The data here is not empty
print(data);
return data;
}
});
} on FirebaseException catch (e) {
throw CustomError(
code: e.code,
message: e.message!,
plugin: e.plugin,
);
} catch (e) {
throw CustomError(
code: 'Exception',
message: e.toString(),
plugin: 'flutter_error/server_error',
);
}
}
Here is the readTodo() of todo_list_cubit.dart
Future<void> readTodo() async {
try {
emit(state.copyWith(
todoListStatus: TodoListStatus.loading,
));
//Start - Part of code where I have issue
Map todoRepoRead = await todoRepository.readTodo();
**after I passed the data from todoRepository.readTodo(), the value returns null**
print(todoRepoRead);
//End - Part of code where I have issue
final rTodos = state.todos.map((Todo todo) {
return Todo(
id: todoRepoRead['id'],
description: todoRepoRead['description'],
completed: todoRepoRead['completed'],
);
}).toList();
emit(state.copyWith(
todoListStatus: TodoListStatus.loaded,
todos: rTodos,
));
} on CustomError catch (e) {
emit(state.copyWith(
todoListStatus: TodoListStatus.error,
error: e,
));
}
}
You should either use async/await or then, but not both.
Using just await, your code becomes:
Future<dynamic> readTodo() async {
try {
final User user = auth.currentUser!;
final uid = user.uid;
DocumentSnapshot documentSnapshot = await usersTodo.doc(uid).get();
if (documentSnapshot.exists) {
var data = documentSnapshot.data() as Map;
//The data here is not empty
print(data);
return data;
};
// TODO: you still need to return something here
} on FirebaseException catch (e) {
throw CustomError(
code: e.code,
message: e.message!,
plugin: e.plugin,
);
} catch (e) {
throw CustomError(
code: 'Exception',
message: e.toString(),
plugin: 'flutter_error/server_error',
);
}
}

How can I convert a Stream<QuerySnapshot<Map<String, dynamic>>> to a List<Object>? Flutter

In the last update of cloud_firestore, I get an error when I run the app with the old code. How can I convert a Stream<QuerySnapshot<Map<String, dynamic>>> to a List?
I have this code and I get null values:
Stream<List<Model>> getReviews(String id) {
try {
return _collectionReference.doc(id).collection('reviews').orderBy('date', descending: true).snapshots().map((reviews) => reviews.docs.map((review) => Model.fromJson(review.data())));
} catch (error) {
return error.message;
}
}
If you just want to get the List<Model> use a get call and await the result before returning the List<Model> like here:
Future<List<Model>> getReviews(String id) {
try {
QuerySnapshot querySnapshot=await _collectionReference.doc(id).collection('reviews').orderBy('date', descending: true).get();
List<Model> result;
querySnapshot.docs.forEach((doc) {
print(doc["first_name"]);
result.add(Model.fromJson(review.data()));
});
return result;
} catch (error) {
return error.message;
}
Make sure to call getReviews as asynchronous.

async Future is not working in my code. How to fix it?

This function always returning null but it is loading data but it is not waiting for Firestore reading. How to solve this issue?
Future<HomePage> read({ String pageName,String mAppId})async{
await Firestore.instance
.collection('tablePages')
.where('projectId', isEqualTo: mAppId)
.where("page.title", isEqualTo:pageName)
.snapshots()
.listen((data) async{
if (data.documents != null
? data.documents.length > 0
? data.documents[0].data != null
: false
: false) {
return HomePage.fromJson(
data.documents[0].data['page']);
} else {
return null;
}
//break;
}).onError((error){
print(error);
return null;
});
}
}
Try the following:
Future<HomePage> read({String pageName, String mAppId}) async {
Stream<QuerySnapshot> snap = Firestore.instance
.collection('tablePages')
.where('projectId', isEqualTo: mAppId)
.where("page.title", isEqualTo: pageName)
.snapshots();
await for (var data in snap) {
if (data.documents != null
? data.documents.length > 0 ? data.documents[0].data != null : false
: false) {
return HomePage.fromJson(data.documents[0].data['page']);
} else {
return null;
}
}
}
From the docs:
Streams can be created in many ways, which is a topic for another article, but they can all be used in the same way: the asynchronous for loop (commonly just called await for) iterates over the events of a stream like the for loop iterates over an Iterable. For example:
Future<int> sumStream(Stream<int> stream) async {
var sum = 0;
await for (var value in stream) {
sum += value;
}
return sum;
}
This code simply receives each event of a stream of integer events, adds them up, and returns (a future of) the sum. When the loop body ends, the function is paused until the next event arrives or the stream is done.
https://dart.dev/tutorials/language/streams

How to get user id from firebase auth as string?

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, ..);
...

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