Flutter :Future builder doesn't gets the data - firebase

I wrote this code which queries Firebase database and returns some data. This thing was working totally fine till few days back but i don't know why now it returns "no data" even if i have data in the database
Is there a error in the code or Something else cause this ?
Fetching the data from Firebase
getsearchfood(String query) {
Future<QuerySnapshot> search =
Firestore.instance.collection("data").getDocuments();
setState(() {
searched = search;
});
}
Operating the data
buildsearchfood() {
return FutureBuilder(
future: searched,
builder: (context, snapshot) {
if (snapshot.hasData) {
print("no data");
}
else{
print("present data");
}
return Container(
);
},
);
}

Add await, as it is an asynchronous operation
Future<QuerySnapshot> search =
await Firestore.instance.collection("data").getDocuments();

You don't need to setState like you did.
Try this:
buildsearchfood() {
return FutureBuilder(
future: Firestore.instance.collection("data").getDocuments(),
builder: (context, snapshot) {
if (snapshot.hasData) {
// This means you have data. You can reference it in `snapshot.data`
}
else{
// This means NO data
}
},
);
}

Related

How to get Metadata for Asynchronous Snapshot in Flutter

So I am pulling data from Cloud Firestore but a bit stuck on checking to see whether the data is being retrieved via the cache or server. So to do this here is how I am pulling the data from cloud firestore
marketplacedata() async {
try {
var snapshot = await FirebaseFirestore.instance
.collection('marketplaces')
.doc('All')
.collection('offers')
.get();
I'm pulling the data from the init
class _SearchMarketplaceState extends State<SearchMarketplace> {
void initState() {
widget.futuredata = getData();
super.initState();
}
getData() async {
return await FireStoreData().marketplacedata('All');
}
Then I am using future builder to retrieve the data as such
FutureBuilder(
future: widget.futuredata,
builder: (BuildContext context, AsyncSnapshot snapshot) {
var marketplacedata = snapshot.data;
if (snapshot.hasError) {
return Text('something went wrong');
}
**if (snapshot.hasData) {
HOW DO I CHECK WHETHER THE DATA IS COMING FROM CACHE?);
.metadata doesnt work on AsyncSnapShot
}**
if (searchController.text.isNotEmpty) {
marketplacedata = searchFilter(
searchController.text.toLowerCase(), marketplacedata);
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Loading();
} else {
return GridView.builder(
gridDelegate:
const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
childAspectRatio: (4 / 4),
),
itemCount: marketplacedata.length ?? 1,
itemBuilder: (BuildContext context, int index) {
return buildMarketplace(context, index,
marketplaceID: marketplacedata[index].marketplaceID,
heading: marketplacedata[index].heading,
companyDesc: marketplacedata[index].companyDesc,
iconURL: marketplacedata[index].iconURL,
keywords: marketplacedata[index].keywords,
preferred: marketplacedata[index].preferred,
imgURL: marketplacedata[index].imgURL);
},
);
}
},
),
Any help is appreciated. In the end I am trying to minimize the number of reads I am getting and hoping to get most of the data read from the cache. However I cant seem to access any data. Thanks in advance.
You can use the data property of the snapshot in your FutureBuilder to access the metadata property:
snapshot.data?.metadata.isFromCache
However, since your overall goal is to reduce backend calls, your get() call to Firestore will always fetch from the server first. See the default value of source in the GetOptions argument to get():
Source.serverAndCache (default value), causes Firestore to try to retrieve an up-to-date (server-retrieved) snapshot, but fall back to returning cached data if the server can't be reached.
You could conditionally use your Future to fetch from the Firestore cache, and subsequently from the server if there is no cached data:
Future<DocumentSnapshot> getData(Source dataSource) {
return FirebaseFirestore.instance
.collection("users")
.doc("testUser1")
.get(GetOptions(source: dataSource)); //calls using GetOptions with Source parameter
}
//using a nested FutureBuilder
return FutureBuilder<DocumentSnapshot>(
future: getData(Source.cache), //first restricts to local cache
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> cacheSnap) {
if (cacheSnap.hasError ||
!cacheSnap.hasData ||
!cacheSnap.data!.exists) {
return FutureBuilder(
future: getData(Source.server), //nested Future uses the server
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> serverSnap) {
if (serverSnap.connectionState == ConnectionState.done) {
return Text(
"Server Future: ${serverSnap.data?.metadata.isFromCache}");
}
return Text("Loading");
});
}
if (cacheSnap.connectionState == ConnectionState.done) {
return Text("Cache Future: ${cacheSnap.data?.metadata.isFromCache}");
}
return Text("loading");
},
);
If you want to listen to real time updates, then the initial state can come from the cache:
If there is a state available in a local cache, the query snapshot will be initially populated with the cached data, then updated with the server's data when the client has caught up with the server's state.
In Flutter, you would use a StreamBuilder instead of a FutureBuilder if you want to go this route. I confirmed this behavior in my project.

How do I get data from multiple documents with StreamBuilder?

I am trying to make my Flutter app with chat.
I use a streambuilder to update the chat data.
My problem is i don't know how i can read multiple documents with streambuilder.
Here my database:
My plan i would like get all data from this documents.
Before, i storage the id in a object and i use a for loop to get all data from documents.
The documents can be a random count (2 or 10...).
Here my streambuilder:
body() {
//build stream link get id
Stream _build_stream_id() async* {
//load user
var user_id = await StorageUserID.loadData();
yield* FirebaseFirestore.instance
.collection('chat')
.doc('users')
.collection(user_id)
.snapshots();
}
//build stream link get data
Stream _build_stream_data(chat_overview_object, index) async* {
yield* FirebaseFirestore.instance
.collection('chat')
.doc('chat_overview_data')
.collection('data')
.doc(chat_overview_object[index].chat_overview_id[0])
.snapshots();
}
return StreamBuilder(
stream: _build_stream_id(),
builder: (context, snapshot) {
if (snapshot.hasData) {
var chat_overview_object = query_chat_overview_data_1(snapshot.data);
for (var i = 0; i < chat_overview_object.length; i++) {
return StreamBuilder(
stream: _build_stream_data(chat_overview_object, i),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text('test: ' + snapshot.data.toString());
} else {
return Text("No data");
}
},
);
}
} else {
return Text("No data");
}
return Text('data');
},
);
}
If you find a better way pls tell me.
If you have questions, feel free to ask me.
Many thx (:
You can combine streams using the StreamGroup from the async package.
import 'package:async/async.dart' show StreamGroup;
List<Stream> streamList = [stream1, stream2];
Stream combinedStream = StreamGroup.merge(streamList);
Though handling Firestore sollections with different fields might be tricky.

how to use StreamBuilder for navigation from one page to another page

I want to check if the user's phone number is present in firestre or not? if it is present in firestore then it will Navigate to the MyHomePage(). It is not Navigate to MyHomePage
My code is
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('users').snapshots(),
builder: (context, snapshot){
if(!snapshot.hasData){
return Text('Please Enter data');
}
final ph=snapshot.data.documents;
List<String> store=[];
for(var phonenum in ph){
final catA=phonenum.data['Phone'];
store=[catA];
for(int i=0;i>=store.length;i++){
if(_phone==store[i]){
Navigator.pushNamed(context, MyHomePage.id);
}
else{
Text('Phone is not registered yet');
}
}
};?
Instead of using Querysnapshot, you can use DocumentSnapshot. It suits well for your situation.
an overview of code:
Future checkIfAvailableOrNot() async{
DocumentSnapshot doc = await db.collection('users').document(_phone).get();
if(doc.exists)
{
//navigate to other page
}
else{
print('no user found');
}
}

Flutter - Dart: how to return a Text widget from my function correctly

so my problem is that I'm trying to load a screen after logging in with an user (firebase auth), this works fine. When logged in, I'm matching the users id from firebase auth, with the same id in firebase cloud, such that I can retrieve the data field "work_title" and display it directly in a Text widget.
So of what i can read off the internet, supposedly everything in the screen is drawn first, only then its possible to retrieve the auth id of the current user, thats why I'm using a futurebuilder.
My question now: why can't I return the return Text(sh.data['work_title'].toString());-part ? I do enter this part of the code, however it only returns the return Text("??");-part.
I've also attached an image of this.
Thanks :)
Widget buildText2() {
return FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: Text("Loading ..."),
);
} else {
if (snapshot.hasData) {
print(snapshot.hasData); // prints true, so we enter this part:
DocumentReference doc = Firestore.instance
.collection("user_profiles")
.document(snapshot.data.uid);
doc.get().then((sh) {
if (sh.exists) {
print(sh.exists); // prints true, så vi enter here as well:
print(sh.data['work_title'].toString());
return Text(sh.data['work_title'].toString()); // <-- I want to return this
}
});
}
} return Text("??");
});
}
A screenshot of the code
Maybe you can try the following (unfortunately i cannot test it..):
FutureBuilder(
future: FirebaseAuth.instance.currentUser(),
builder: (context, AsyncSnapshot<FirebaseUser> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(child: Text("Loading ..."));
} else {
if (snapshot.hasData) {
print(snapshot.hasData); // prints true, so we enter this part:
DocumentReference doc = Firestore.instance
.collection("user_profiles")
.document(snapshot.data.uid);
return FutureBuilder(
future: doc.get(),
builder: (context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
if (snapshot.data.exists) {
return Text(snapshot.data.data['work_title'].toString()); // <-- I want to return this
} else {
return Text('sh does not exists..');
}
} else {
return Text('Still Loading...');
}
},
);
}
}
return Text("??");
})

Why is print(snapshot.hasData) returning true?

I must be misunderstanding the hasData method for a QuerySnaphot. In my StreamBuilder I want to return a widget informing the user there are no items in the collection queried. I've deleted the collection in Firestore so there's definitely no data there. But when I run the following code:
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('Events')
.where("bandId", isEqualTo: identifier)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (!snapshot.hasData) {
print('code here is being executed 1');// This gets executed
return Text('helllllp');
} else {
print('Code here is being executed2'); //And this gets executed
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return new Text('Loading...');
default:
return new ListView(
children:
snapshot.data.documents.map((DocumentSnapshot document) {
return CustomCard(
event: document['event'],
location: document['location'],
service: document['service'],
date: document['date'].toDate(),
);
}).toList(),
);
}
}
},
),
All I want to do is return a widget informing user if the snapshot is empty. For example Text('You have no messages')
The problem here is that snapshots() will also return a QuerySnapshot when the query returns no documents. Thus, you could expand your condition like this:
if (!snapshot.hasData || snapshot.data.documents.isEmpty) {
return Text('You have no messages.');
} else {
...
}
Although, realistically you should not return You have no messages when snapshot.data is null because it is null before the query is completed. Hence, I would go for something like this:
if (!snapshot.hasData) {
return Text('Loading...');
}
if (snapshot.data.documents.isEmpty) {
return Text('You have no messages.');
}
return ListView(..);
This ignores error handling, however, that can also be added.
Notice that snapshot.hasData is an alternative to determining connection state using snapshot.connectionState.

Resources