I'm expecting my random value to be an int but I got a String instead,
is this the right way to do it.
StreamBuilder(
stream: FirebaseDatabase.instance.ref().child('RandomVal').onValue,
builder: (context, snapshot) {
if (snapshot.hasData && !snapshot.hasError) {
final event = snapshot.data as DatabaseEvent;
final data = event.snapshot.value as Map;
print(data['Value']); // my value as expected
print(data['Value'].runtimeType); // String instead of int
}
return Text('please wait');
},
),
Related
In my application, I have two future builders:
CollectionReference stream = Firestore.instance.collection('users');
List<String> myIDs =[];
List <dynamic> mylist =[];
List<String> myNames =[];
String? userName;
Widget userValues() {
return FutureBuilder(
future: getrecords(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data? [index] ?? "got null");
},
);
}
else {
return CircularProgressIndicator();
}
},
);
}
..................
Future getrecords() async{
final data = await stream.get();
mylist.addAll(data);
mylist.forEach((element) {
final String firstPartString = element.toString().split('{').first;
final String id = firstPartString.split('/').last;
myIDs.add(id.trim());
});
return(myIDs);
}
....................
Widget Names() {
return FutureBuilder(
future: getNames(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data!.length,
itemBuilder: (context, index) {
return Text(snapshot.data?[index] ?? "got null");
},
);
}
else {
return CircularProgressIndicator();
}
},
);
}
............................
Future getNames() async{
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}
The first future (userValues) works fine, and I get the result just fine, but the other one with the for loop is not working properly and is not returning values until I hot reload, then a name will be added to the list, and so on with each hot reload.
What I want to achieve is to keep the loading indicator until the for loop is over, then build the screen.
UPDATE:
If I could manage to make it so that the "Names" futurebuilder awaits for the userValues to complete before starting, then my problem would be solved, but what I realized is that it's taking the initial value of the return from "userValues," which is non, and using it to build.
Future getNames() async{
await Future.delayed(const Duration(seconds: 2));
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}
When I added this 2 seconds delay, it worked properly but is there any other way to make it wait for the first future to complete then start the second one?
You can use the await keyword on the future returned from getrecords() to wait for the completion of getrecords() before starting the getNames() function:
Future getNames() async{
await getrecords();
for (var id in myIDs ){
var names = stream.document(id).collection('userName').document('userName');
var document = await names.get();
userName = document['name'];
myNames.add(userName!);
}
return(myNames);
}
Flutter Streambuilder code below runs without error and returns (screenshot at bottom):
ID: AzFdOO9WsFaFbTxTQsuo
Data: Instance of '_JsonDocumentSnapshot'
How do I get to the values inside the _JsonDocumentSnapshot and display them in the Text() widget?
For instance, there's a string field called "name", but I can't figure out how to get to it.
StreamBuilder(
stream: FirebaseFirestore.instance
.collection("groceries")
.doc(widget.docId)
.snapshots(),
builder: (context, streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
} else if (streamSnapshot.hasData) {
return Text("ID: ${widget.docId}\n"
"Data: ${streamSnapshot.data}");
} else {
return const Text("No Data");
}
}
)
Thanks for your help!
the following Stream, return an object with a type of DocumentSnapshot :
FirebaseFirestore.instance.collection("groceries").doc(widget.docId).snapshots();
and that type contains the document snapshot, and also contains more additional information about the document.
so in order to get the JSON Map<String, dynamic> which represents the data of the Firestore document, you need to call data() on the result of the snapshot.data(), so you need to try the following:
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance
.collection("groceries")
.doc(widget.docId)
.snapshots(),
builder: (BuildContext context, AsyncSnapshot<DocumentSnapshot> streamSnapshot) {
if (streamSnapshot.connectionState == ConnectionState.waiting) {
return const Text("Loading");
} else if (streamSnapshot.hasData) {
return Text("ID: ${widget.docId}\n"
"Data: ${streamSnapshot.data.data()}"); // added data()
} else {
return const Text("No Data");
}
}
)
now it should show the Map<String, dynamic> object which contains your document data in the Text widget.
hope this helps.
In your code example streamSnapshot.data is an Object or a dynamic type variable.
To access the json value of your data, you have to specify the key corresponding to your value.
streamSnapshot.data['banana']
I have a StreamBuilder to check for an empty string But I want to turn it to check for an empty array. How can I do that?
bool? checkEmpty = false;
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('widgets')
.doc(widgets)
.snapshots(),
builder: (context, snapshot) {
snapshot.data?.data()?.forEach((key, value) {
if (key == 'imageUrl') {
checkEmpty = value == [];
}
});
return ...
checkEmpty!
? Text('Array is not empty')
: Text('Empty array'),
cast the value type as List then check over it.
try the following:
bool? checkEmpty = false;
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream: FirebaseFirestore.instance
.collection('widgets')
.doc(widgets)
.snapshots(),
builder: (context, snapshot) {
snapshot.data?.data()?.forEach((key, value) {
if (key == 'imageUrl') {
checkEmpty = (value as List).isEmpty;
}
});
return ...
checkEmpty!
? Text('Array is not empty')
: Text('Empty array'),
This is my stream function:
Stream<QuerySnapshot> getPortfolios() {
return db
.collection('users')
.doc(authService.getUser().uid)
.collection('portfolios')
.snapshots();
}
In the StreamBuilder I get the list like this:
portfolios = snapshot.data!.docs;
That gives my a list but a list of JsonQueryDocumentSnapshot. With each item I can do .data() and I get the info I need, but how could I get this map directly in the list without calling extra methods?
I tried this old answer but it doesn't work anymore:
final QuerySnapshot<Object?>? ds = snapshot.data;
final Map<String, dynamic> map = ds!.data; // this .data is not recognised
You can use ".docs" on the snapshot to get a list of QueryDocumentSnapshot which have some of the same properties as a map, so you can directly reference a value like im doing below:
List<QueryDocumentSnapshot> data = snapshot.data!.docs;
String name = data[i]['name']
Here is a full example :
StreamBuilder<QuerySnapshot>(
stream: db.collection('users').snapshots(),
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
List<QueryDocumentSnapshot> data = snapshot.data!.docs;
return ListView.builder(
itemCount: snapshot.data!.size,
itemBuilder: (context, i) {
return Text('${data[i]['name']}');
},
);
} else {
return CircularProgressIndicator();
}
},
),
Alternatively you can map the Stream to any object like this:
Stream<QuerySnapshot<Map<String, dynamic>>> snaphot = db
.collection('users')
.doc(authService.getUser().uid)
.collection('portfolios')
.snapshots();
//Map to an object (note that you need to create a "fromJson" method for your object.
Stream<List<YourObject>> dataStream = snaphot.map((list) => list.docs.map((doc) => YourObject.fromJson(doc.data())).toList());
Now you can use the "dataStream" in your stream builder and directly reference the items in the list with "snapshot.data".
// snapshot.data is now the type:
List<YourObject>
List<Photo> imgList = [];
Future getCarouselWidget() async {
var firestore = Firestore.instance;
QuerySnapshot qn =
await firestore.collection("history").getDocuments();
List pics = qn.documents
.map((it) => Photo(it['photo'].toString(), it['name'].toString(), it['address'].toString()))
.toList();
return imgList = pics;
}
Hi all, when I make changes in my DB I dont see them in my app? can anybody help me or guid me how to wire this to stream builder or query the stream
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('swimfinderlakes')
.snapshots(),
builder: (context, snapshot) {
Every time Firestore has a change, it'll trigger the StreamBuilder. U can then access the most recently update with the snapshot on the builder method. Then you can use it to update the ui accordingly.
StreamBuilder<QuerySnapshot>(
stream: Firestore.instance
.collection('swimfinderlakes')
.snapshots(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
if (snapshot.hasData) {
List pics = qn.documents
.map((it) => Photo(it['photo'].toString(),
it['name'].toString(), it['address'].toString())).toList();
return pics;
}
} else {
return CircularProgressIndicator();
}
}