I am trying to get a value from firestore and save it to a variable in flutter. I tried using StreamBuilder, but since i am not building a widget it does not work.
To clarify my problem, I am trying to get a url from a firestore document and then open it when i press a button in the app.
I also tried to adapt code i found in another question, but that returns null.
Future _getUrl() async{
DocumentReference docRef = Firestore.instance.collection('information').document('pdf');
var data;
docRef.get().then((datasnapshot){
data = datasnapshot.data['url'];
});
return data;
}
The collection is called information, the document pdf, and the field url
This method will return null because you are not waiting for the get() future to return before you return data. docRef.get() is a Future, so it will execute asychronously. Meanwhile, your program will move on to the next line, which is return data.
Something like this would do what you want I think:
Future _getUrl() async{
DocumentReference docRef = Firestore.instance.collection('information').document('pdf');
return docRef.get().then((datasnapshot){
return datasnapshot.data['url'];
});
}
Since _getUrl is already marked as async you can also use await in its body to return the right value:
Future _getUrl() async {
DocumentReference docRef = Firestore.instance.collection('information').document('pdf');
await datasnapshot = docRef.get();
let data = datasnapshot.data['url'];
return data;
}
Related
I'm experiencing an annoying thing, where the first item in my listview keeps re-render, when scrolling up. Even if I'm at the top.
only way i noticed this, was because I have a widget, that on load, fetches an url, and get the meta title, description and image, and displaying it in a nice card.
My listviews are fairly simple:
ListView.builder(
physics: AlwaysScrollableScrollPhysics(),
controller: _scrollController,
itemCount: model.posts.posts.length,
itemBuilder: (context, index) {
// Items goes here
});
How do I stop it from happening?
The widget that keeps re-rendering, is a stateless widget that imports a ScopedModel model, that fetches some data from the internet, and scraped for meta data, and then updated the model.
#override
Widget build(BuildContext context) {
UrlEmbedModel _model = new UrlEmbedModel(); // <-- the ScopedModel
_model.fetchHtml(url); // <-- the url param comes from the constuctor
// Rest of the widget
}
Here is the code that fetches content from the net.
void fetchHtml(url) {
http.get(url).then((response) {
if (response.statusCode == 200) {
// If server returns an OK response, parse the JSON
var document = parse(response.body);
var list = document.getElementsByTagName('meta');
for (var item in list) {
if (item.attributes['property'] == "og:title") {
_title = item.attributes['content'];
}
if (item.attributes['property'] == "og:description") {
_description = item.attributes['content'];
}
if (item.attributes['property'] == "og:image") {
_imageUrl = item.attributes['content'];
}
notifyListeners();
}
} else {
// If that response was not OK, throw an error.
throw Exception('Failed to load post');
}
});
}
The code you wrote, seems OK, but what about the function that makes the request? Can you show it?
If it's a Future function, it'll only make a request once and then finish it, it's not like a stream function that will be always listening to an event.
EDIT
First of all, if this functions makes a request, then, the type of the functions must be Future, void type if don't return anything, after that, add the async call. You could change the .then method to an await method, it'll suit you better.
Future<void> fetchHtml(url) async {
final Response response = await get(url);
final dynamic documents = json.decode(response.body); //import 'dart:convert';
print(documents); // print to see what you get than, pass it to the variables you want the data
if (response.statusCode == 200) {
//in here
}
}
I can see a feel things in the fetch request, I'd be glad if you answer it:
Why you're not deserializing the json you receiving?
var documents = json.decode(response.body)
You could print the documents variable after deserializing it and atribute it to the widgets you want
The way you're doing it it's not wrong, but could improve it.
Found the culprit.
The issue wasn't the listview, it was the RefreshIndicator that I used.
As soon I removed it, the issue went away.
This seems to be a bug with Widget.
List returnMovies = [];
Future<List> _getData() async {
final response = await http.get("https:../getTodayMovies",
headers: {HttpHeaders.AUTHORIZATION: Acess_Token.access_token});
if (response.body != null) {
returnMovies = json.decode(response.body);
.....
setState(() {});
} else {
final responseUpcoming = await http.get("/upcoming",
headers: {HttpHeaders.AUTHORIZATION: Acess_Token.access_token});
returnMovies = json.decode(responseUpcoming.body);
}
The response.body looks like:
[{"id":394470548,"host_group_name":"heyab redda","movie_image":"..png","type":"horror","code":"X123","name":"Lovely","start_time":1554364800,"end_time":1554393600,"}]
The responseUpcoming.body looks like:
{"id":394470545,"host_group_name":"foo redda","movie_image":".png","type":"horror","code":"X123","name":"Lovely","start_time":1554364800,"end_time":1554393600,"}, {"id":394470548,"host_group_name":"foo1 redda","movie_image":"..png","type":"comic","code":"X125","name":"Lovely1","start_time":1554364800,"end_time":1554393600,"}
The error I get is: String, dynamic is not a subtype of type List<dynamic>.
In the first API call that I am doing I normally get in return an array of objects, however, when this is empty, the second API call returns a list of objects that I want to push into the array called returnMovies, how can I achieve this?? Is there any way to .push these objects in the array?? So then I want to use this array to build dynamically a Listview.builder.
Also is it correct the way I am declaring it? I am quite new on Dart. Thank you
Sounds like you are looking for addAll
returnMovies.addAll(json.decode(returnUpcoming.body))
I will suggest to use
returnMovies.addAll({your object here})
When you do this json.decode(response.body) you are getting a List of Map you should use List<dynamic> movieListData and get the items like this:
movieListData = json.decode(response.body);
returnMovies = movieListData.map((dynamic movieData) {
String id = movieData['_id'];
String host_group_name = movieData['host_group_name'];
String duration = movieData['duration'];
return new Movie(id,title, duration);
}).toList();
Is there any way for retrieving the items from one API call and that API is dependent on another API call.
First, create two methods to call each API
Future<String> makeFirstCall() {
String url;
url = // Call your first api and return the url
return url;
}
Future<String> makeSecondCall(String url) {
// Call your second API with the given url and return the item-name
}
Then in your code make the following call to chain both apis:
makeFirstCall().then((url) =>
makeSecondCall(url).then((itemName) {
// Use itemName to do whatever you want
});
);
You can also use await instead of then()
String url = await makeFirstCall();
String itemName = await makeSecondCall(url);
// Use itemName to do whatever you want
I've seen this question answered multiple times already for JavaScript and other languages. There, it always comes down to get a snapshot and use a method called exists() to check. But in Dart/Flutter, there is no such method. Here's what I have for now:
devicesRef.child(deviceId).once().then((DataSnapshot data) {
print(data.key);
print(data.value);
});
I want to check whether a node called deviceId already exists.
So how can I check if a node exists in Firebase Realtime Database with Dart/Flutter?
I would guess, since there is no such thing as a null child value in Realtime Database, that you could simply check if data.value is null.
Pass Your DatabaseReference to this method , and will return true if it Exists
Future<bool> rootFirebaseIsExists(DatabaseReference databaseReference) async{
DataSnapshot snapshot = await databaseReference.once();
return snapshot !=null;
}
Future<bool> isUserRegistered(String idToken) async{
DataSnapshot dataSnapshot = await databaseReference.child('app').child('users').child(idToken).once();
return dataSnapshot.value!=null;
}
Updated answer:
static Future<bool> isNodeExists(DatabaseReference databaseReference) async {
var result = await databaseReference.once();
return result.snapshot.value != null;
}
Im using the following code to update a Cloud Firestore collection using Dart/Flutter.
final docRef = Firestore.instance.collection('gameLevels');
docRef.document().setData(map).then((doc) {
print('hop');
}).catchError((error) {
print(error);
});
I'm trying to get the documentID created when I add the document to the collection but the (doc) parameter comes back as null. I thought it was supposed to be a documentReference?
Since it's null, I obviously can't use doc.documentID.
What am I doing wrong?
You can try the following:
DocumentReference docRef = await
Firestore.instance.collection('gameLevels').add(map);
print(docRef.documentID);
Since add() method creates new document and autogenerates id, you don't have to explicitly call document() method
Or if you want to use "then" callback try the following:
final collRef = Firestore.instance.collection('gameLevels');
DocumentReferance docReference = collRef.document();
docReferance.setData(map).then((doc) {
print('hop ${docReferance.documentID}');
}).catchError((error) {
print(error);
});
#Doug Stevenson was right and calling doc() method will return you document ID. (I am using cloud_firestore 1.0.3)
To create document you just simply call doc(). For example I want to get message ID before sending it to the firestore.
final document = FirebaseFirestore.instance
.collection('rooms')
.doc(roomId)
.collection('messages')
.doc();
I can print and see document's id.
print(document.id)
To save it instead of calling add() method, we have to use set().
await document.set({
'id': document.id,
'user': 'test user',
'text': "test message",
'timestamp': FieldValue.serverTimestamp(),
});
Using value.id ,You can fetch documentId after adding to FireStore .
CollectionReference users = FirebaseFirestore.instance.collection('candidates');
Future<void> registerUser() {
// Call the user's CollectionReference to add a new user
return users.add({
'name': enteredTextName, // John Doe
'email': enteredTextEmail, // Stokes and Sons
'profile': dropdownValue ,//
'date': selectedDate.toLocal().toString() ,//// 42
})
.then((value) =>(showDialogNew(value.id)))
.catchError((error) => print("Failed to add user: $error"));
}
If the Dart APIs are anything like other platforms, the document() method should return a document reference that has an id property with the randomly generated id for the document that's about to be added to the collection.
This worked for me
//add a passed employee to firebase collection called employees
static Future<void> addEmployee(Employee employee) async {
final CollectionReference employees = FirebaseFirestore.instance.collection('employees');
var doc = employees.doc();
employee.employeeID = doc.id;
await doc.set(employee.toJson());
}