Why future forEach is looping more than expected? - firebase

I have two functions,
First one to get the users from Firestore. Then I use "Future.ForEach" to loop on those users.
Future<List> getData() async {
targetData.clear();
await Firestore.instance
.collection('users')
.document(currentUserId)
.collection('chats')
.getDocuments()
.then((userChats) async {
// Only two documents are coming from the db
return await Future.forEach(userChats.documents, getTargetData)
.then((onValue) {
print('forEech is done');
});
});
print('getData Returen');
// I use "targetData" to build viewList
return targetData;
}
Calling this function from the Future.forEach
Future<List> getTargetData(DocumentSnapshot targetDoc) async {
print('looping');
await Firestore.instance
.collection('users')
.document(targetDoc.documentID)
.get()
.then((targetRef) {
targetData.add(new TargetUserData(
targetRef.documentID,
targetRef.data['nickname'],
targetRef.data['photoUrl'],
targetDoc.data['timestamp'],
targetRef.data['token'],
targetDoc.data['unseen']));
});
return targetData;
}
I'm calling getData from inside the FutureBuilder
return new FutureBuilder(
future: getData(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
return buildLoading();
default:
if (snapshot.hasError)
return new Text('Error: ${snapshot.error}');
else
return createListView(context, snapshot);
}
},
);
When I run the app it works perfectly fine and as expected - Here is the log from the print statement:
I/flutter (18780): looping
I/flutter (18780): looping I/flutter
(18780): forEech is done
I/flutter (18780): getDataReturen
Hot Restart - First Run
I expect on hot reload that I get the same result But, what is coming is!!
I/flutter (18780): looping
I/flutter (18780): looping
I/flutter (18780): looping
I/flutter (18780): forEech is done
I/flutter (18780): getDataReturen
I/flutter (18780): forEech is done
I/flutter (18780): getDataReturen
Hot Reloads ...
Question: Why the forEach is looping more than expected?

I was able to solve it by reframing the code:
Future<List> _getData() async {
var allUsers = await Firestore.instance.collection('users').getDocuments();
List<TargetUserData> users = [];
for (var userData in allUsers.documents) {
if (currentUserId != userData.documentID) {
await getTargetData(userData.documentID).then((targetData) {
TargetUserData targetUserData = TargetUserData(
userData.documentID,
userData.data['nickname'],
userData.data['photoUrl'],
targetData[0],
userData.data['token'],
targetData[1],
targetData[2]);
users.add(targetUserData);
});
}
}
return users;
}
Future getTargetData(var targetId) async {
List targetData = [];
await Firestore.instance
.collection('users')
.document(currentUserId)
.collection('chats')
.document(targetId)
.get()
.then((advData) {
if (advData.data != null) {
targetData.add(advData.data['timestamp']);
targetData.add(advData.data['unseen']);
targetData.add(advData.data['lastMsg']);
} else {
targetData.add('0');
targetData.add('0');
targetData.add('Start Chating!');
}
}).catchError((onError) {
print('on error: $onError');
});
return targetData;
}

Related

Convert method into FutureBuilder : Flutter

I'm new to flutter. This is the method that I used to retrieve the data from firebase and I'm able to get the exact answer in the console. My question is how I can convert this code into future builder so I am able to read the data in my application.
void getUser() async {
firestoreInstance.collection("User Data").get().then((querysnapshot) {
querysnapshot.docs.forEach((result) {
firestoreInstance
.collection("User Data")
.doc(result.id)
.collection("bank")
.where('account_username', isEqualTo: ownerData?.name2)
.get()
.then((query Snapshot) {
querysnapshot.docs.forEach((result) {
print (result["bank_name"]);
});
});
});
});
}
You should return the value from the query, not print it
Your function should look like this
Future<String> getUser() async {
firestoreInstance.collection("User Data").get().then((querysnapshot) {
querysnapshot.docs.forEach((result) {
firestoreInstance
.collection("User Data")
.doc(result.id)
.collection("bank")
.where('account_username', isEqualTo: ownerData?.name2)
.get()
.then((query Snapshot) {
querysnapshot.docs.forEach((result) {
return result["bank_name"];
});
});
});
});
}
The Futurebuilder should look like this
FutureBuilder<String>(
future: getUser(), // async work
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting: return Text('Loading....');
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return Text('Result: ${snapshot.data}');
}

flutter: check if the document exist

I want to check whether the document exist or not without creating the document if it does not exits
Checked() {
Future<DocumentSnapshot> check = linkref.
document(user.UID).
collection("Requests").
document(uuid).get();
return FutureBuilder(
future: check,
builder: (context, ccheck) {
if (check != null ) {
return Text("Available");
}
return Text("not available);
});
}
i tried this code but even if the document does not exists it says that it exists
You should use; if (ccheck.data.exists) instead of if (check != null ). Here is the code;
Checked() {
Future<DocumentSnapshot> check =
linkref.document(user.UID).collection("Requests").document(uuid).get();
return FutureBuilder<DocumentSnapshot>(
future: check,
builder: (context, ccheck) {
if (ccheck.data.exists) {
return Text("Available");
}
return Text("not available");
});
}
You can use the .where( field, isEqualTo: query). This might be useful to you.
final userRef = FirebaseFirestore.instance.collection('users');
checkExists(String query) async {
QuerySnapshot checker = await userRef
.where('uid', isEqualTo: query)
.get();
chkr.docs.forEach((doc) {
if (doc.exists) {
print('Exists');
print(doc.get('uid'));
}else {
print('false');
}
});
}
Then, if you are using a button, you can use onPressed: () => check(yourQuery).

Error when trying to get data from Firestore

when I try to retrieve a data from firestore with this code.
Future<String> getUserType() async {
await (Firestore.instance
.collection('users')
.document(getUserUID().toString())
.get()
.then((DocumentSnapshot ds) {
return ds['type'];
}));
}
i get this error
NoSuchMethodError: The method '[]' was called on null.
I/flutter (15824): Receiver: null
I/flutter (15824): Tried calling: []("type")
I also tried:
return ds.data['type'];
The code to retrive the uid of user is:
Future<String> getUserUID() async {
return (await _firebaseAuth.currentUser()).uid;
}
but I don't think that this is the problem, maybe in ds there is nothing.
You need to retrieve the userID first and then use that in your document retrieval:
Future<String> getUserType() async {
String userID = (await _firebaseAuth.currentUser()).uid;
await (Firestore.instance
.collection('users')
.document(userID)
.get()
.then((DocumentSnapshot ds) {
return ds['type'];
}));
}
In your code:
Future<String> getUserUID() async {
return (await _firebaseAuth.currentUser()).uid;
}
getUserUID() returns a Future, but when you do .document(getUserUID().toString()) you are not getting the result of that Future.
Check the following:
https://dart.dev/codelabs/async-await
Your getUserUID() method returns a Future String not a regular String.So that you cannot directly get a document by providing that.This is the usual way I implement a function like this.
Future<String> getUserType() async {
getUserUID().then((currentUser) {
if (currentUser != null) {
await (Firestore.instance
.collection('users')
.document(currentUser)
.get()
.then((DocumentSnapshot ds) {
return ds['type'];
}));
}
}
}

Async await flutter firestore

I would like to ask whats going on with my code.
Assuming the 'Counter' field is 179 in this instance, how do I make my outside myData update before printing?
class Test {
Firestore _firestore = Firestore.instance;
var myData;
void getData() async {
DocumentSnapshot snapshot =
await _firestore.collection('Counter').document('Counter').get();
myData = await snapshot.data['Counter'];
print('inside $myData');
}
void checkMyData() {
myData = 5;
getData();
print('outside $myData');
}
}
Console:
flutter: outside 5
flutter: inside 179
You have to make getData() return a Future like this:
Future getData() async {
So you can do this:
getData().then((value) {
print('value: $value');
}).catchError((error) {
print('error: $error');
});
But you probably want to use a FutureBuilder to show the information when arrives, like this:
FutureBuilder(
future: getData(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('value: ${snapshot.data}');
} else if (snapshot.hasError){
return Text('error: ${snapshot.error}');
}
return Text('loading...');
},
)

Async await database taking time

In my case, I need to load some data from database at the beginning of the application. But I cannot load data, so I tested what is wrong with my code and found out it takes some time in await database and the code skip this function and execute other operations. In result , I get the data later after creating the UI, so cannot show data. Any way to fix that?
This is my code and result-
dbhelper.dart
static Database db_instance;
Future<Database> get db async{
if(db_instance == null)
db_instance = await initDB();
return db_instance;
}
initDB() async{
var dbDir = await getDatabasesPath();
var dbPath = join(dbDir, "test.db");
ByteData data = await rootBundle.load(join("assets","test.db"));
List<int> bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes);
await File(dbPath).writeAsBytes(bytes);
var db = await openDatabase(dbPath);
return db;
}
getTest() async{
print("Test1");
var db_connection = await db;
print("Test2");
List<Map> list = await db_connection.rawQuery('SELECT * FROM $TABLE_NAME Where id= 1');
print("Test3");
for(int i=0;i<list.length;i++){
print("Test4");
id = list[i]['id'];
name = list[i]['name'];
phone = list[i]['phone'];
}
print("Test5"+name);
}
main.dart
getContactsFromDB()async{
var dbHelper = DBHelper();
dbHelper.getTest();
print("Test6"+phone);
}
#override
void initState() {
getContactsFromDB();
super.initState();
}
Result
I/flutter (13203): Test1
I/flutter (13203): Test6 // skip Test2,3,4,5 and jump to another operation and data not collected
I/flutter (13203): Test2
I/flutter (13203): Test3
I/flutter (13203): Test4
I/flutter (13203): Test5 Zin
Just use FutureBuilder. Something like here:
Widget projectWidget() {
return FutureBuilder(
future: getContactsFromDB(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Container();
} else {
return CircularProgressIndicator();
}
}
)
}

Resources